<commit_before>/*
Package framed provides an implementation of io.ReadWriteCloser that reads and
writes whole frames only.

Frames are length-prefixed.  The first two bytes are an unsigned 16 bit int
stored in little-endian byte order indicating the length of the content.  The
remaining bytes are the actual content of the frame.

The use of a uint16 means that the maximum possible frame size (MaxFrameSize)
is 65535.
*/
package framed

import (
	"encoding/binary"
	"fmt"
	"io"
	"sync"
)

const (
	// MaxFrameSize is the maximum possible size of a frame (not including the
	// length prefix)
	MaxFrameSize = 65535
)

var endianness = binary.LittleEndian

/*
A Reader enhances an io.ReadCloser to read data in contiguous frames. It
implements the io.Reader interface, but unlike typical io.Readers it only
returns whole frames.

A Reader also supports the ability to read frames using dynamically allocated
buffers via the ReadFrame method.
*/
type Reader struct {
	Stream io.Reader // the raw underlying connection
	mutex  sync.Mutex
}

/*
A Writer enhances an io.WriteCLoser to write data in contiguous frames. It
implements the io.Writer interface, but unlike typical io.Writers, it includes
information that allows a corresponding Reader to read whole frames without them
being fragmented.

A Writer also supports a method that writes multiple buffers to the underlying
stream as a single frame.
*/
type Writer struct {
	Stream io.Writer // the raw underlying connection
	mutex  sync.Mutex
}

func NewReader(r io.Reader) *Reader {
	return &Reader{Stream: r}
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{Stream: w}
}

/*
Read implements the function from io.Reader.  Unlike io.Reader.Read,
frame.Read only returns full frames of data (assuming that the data was written
by a framed.Writer).
*/
func (framed *Reader) Read(buffer []byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	var nb uint16
	err = binary.Read(framed.Stream, endianness, &nb)
	if err != nil {
		return
	}

	n = int(nb)

	bufferSize := len(buffer)
	if n > bufferSize {
		return 0, fmt.Errorf("Buffer of size %d is too small to hold frame of size %d", bufferSize, n)
	}

	// Read into buffer
	n, err = io.ReadFull(framed.Stream, buffer[:n])
	return
}

// ReadFrame reads the next frame, using a new buffer sized to hold the frame.
func (framed *Reader) ReadFrame() (frame []byte, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	var nb uint16
	err = binary.Read(framed.Stream, endianness, &nb)
	if err != nil {
		return
	}

	l := int(nb)
	frame = make([]byte, l)

	// Read into buffer
	n, err := io.ReadFull(framed.Stream, frame)
	if n != l {
		return nil, fmt.Errorf("Read wrong number of bytes. Expected frame of length %d, got %d", n, l)
	}
	return
}

/*
Write implements the Write method from io.Writer.  It prepends a frame length
header that allows the framed.Reader on the other end to read the whole frame.
*/
func (framed *Writer) Write(frame []byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	n = len(frame)

	// Write the length header
	if err = binary.Write(framed.Stream, endianness, uint16(n)); err != nil {
		return
	}

	// Write the data
	var written int
	if written, err = framed.Stream.Write(frame); err != nil {
		return
	}
	if written != n {
		err = fmt.Errorf("%d bytes written, expected to write %d", written, n)
	}
	return
}

func (framed *Writer) WritePieces(pieces ...[]byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	for _, piece := range pieces {
		n = n + len(piece)
	}

	// Write the length header
	if err = binary.Write(framed.Stream, endianness, uint16(n)); err != nil {
		return
	}

	// Write the data
	var written int
	for _, piece := range pieces {
		var nw int
		if nw, err = framed.Stream.Write(piece); err != nil {
			return
		}
		written = written + nw
	}
	if written != n {
		err = fmt.Errorf("%d bytes written, expected to write %d", written, n)
	}
	return
}
<commit_msg>Added constant for frame header size<commit_after>/*
Package framed provides an implementation of io.ReadWriteCloser that reads and
writes whole frames only.

Frames are length-prefixed.  The first two bytes are an unsigned 16 bit int
stored in little-endian byte order indicating the length of the content.  The
remaining bytes are the actual content of the frame.

The use of a uint16 means that the maximum possible frame size (MaxFrameSize)
is 65535.
*/
package framed

import (
	"encoding/binary"
	"fmt"
	"io"
	"sync"
)

const (
	// FrameHeaderSize is the size of the frame header in bytes
	FrameHeaderSize = 2
	// MaxFrameSize is the maximum possible size of a frame (not including the
	// length prefix)
	MaxFrameSize = 65535
)

var endianness = binary.LittleEndian

/*
A Reader enhances an io.ReadCloser to read data in contiguous frames. It
implements the io.Reader interface, but unlike typical io.Readers it only
returns whole frames.

A Reader also supports the ability to read frames using dynamically allocated
buffers via the ReadFrame method.
*/
type Reader struct {
	Stream io.Reader // the raw underlying connection
	mutex  sync.Mutex
}

/*
A Writer enhances an io.WriteCLoser to write data in contiguous frames. It
implements the io.Writer interface, but unlike typical io.Writers, it includes
information that allows a corresponding Reader to read whole frames without them
being fragmented.

A Writer also supports a method that writes multiple buffers to the underlying
stream as a single frame.
*/
type Writer struct {
	Stream io.Writer // the raw underlying connection
	mutex  sync.Mutex
}

func NewReader(r io.Reader) *Reader {
	return &Reader{Stream: r}
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{Stream: w}
}

/*
Read implements the function from io.Reader.  Unlike io.Reader.Read,
frame.Read only returns full frames of data (assuming that the data was written
by a framed.Writer).
*/
func (framed *Reader) Read(buffer []byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	var nb uint16
	err = binary.Read(framed.Stream, endianness, &nb)
	if err != nil {
		return
	}

	n = int(nb)

	bufferSize := len(buffer)
	if n > bufferSize {
		return 0, fmt.Errorf("Buffer of size %d is too small to hold frame of size %d", bufferSize, n)
	}

	// Read into buffer
	n, err = io.ReadFull(framed.Stream, buffer[:n])
	return
}

// ReadFrame reads the next frame, using a new buffer sized to hold the frame.
func (framed *Reader) ReadFrame() (frame []byte, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	var nb uint16
	err = binary.Read(framed.Stream, endianness, &nb)
	if err != nil {
		return
	}

	l := int(nb)
	frame = make([]byte, l)

	// Read into buffer
	n, err := io.ReadFull(framed.Stream, frame)
	if n != l {
		return nil, fmt.Errorf("Read wrong number of bytes. Expected frame of length %d, got %d", n, l)
	}
	return
}

/*
Write implements the Write method from io.Writer.  It prepends a frame length
header that allows the framed.Reader on the other end to read the whole frame.
*/
func (framed *Writer) Write(frame []byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	n = len(frame)

	// Write the length header
	if err = binary.Write(framed.Stream, endianness, uint16(n)); err != nil {
		return
	}

	// Write the data
	var written int
	if written, err = framed.Stream.Write(frame); err != nil {
		return
	}
	if written != n {
		err = fmt.Errorf("%d bytes written, expected to write %d", written, n)
	}
	return
}

func (framed *Writer) WritePieces(pieces ...[]byte) (n int, err error) {
	framed.mutex.Lock()
	defer framed.mutex.Unlock()

	for _, piece := range pieces {
		n = n + len(piece)
	}

	// Write the length header
	if err = binary.Write(framed.Stream, endianness, uint16(n)); err != nil {
		return
	}

	// Write the data
	var written int
	for _, piece := range pieces {
		var nw int
		if nw, err = framed.Stream.Write(piece); err != nil {
			return
		}
		written = written + nw
	}
	if written != n {
		err = fmt.Errorf("%d bytes written, expected to write %d", written, n)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"strings"
	"encoding/gob"
	"fmt"
	"bufio"
	"io"
	"log"
	"net"
	"time"
	"os"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	"github.com/titanous/go-dockerclient"
	"github.com/flynn/sampi/types"
	sc "github.com/flynn/sampi/client"
	lc "github.com/flynn/lorne/client"
)

// WARNING: assumes one host at the moment (firstHost will always be the same)

var sd *discover.Client
var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sd, err = discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"
	hostname := shell("curl -s icanhazip.com")

	set, _ := sd.Services("shelf")
	addrs := set.OnlineAddrs()
	if len(addrs) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := addrs[0]

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s on %s ...\n", app, hostname)

	scheduleAndAttach(app + ".build", docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := app + ".web"

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start", "web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
		},
	})

	time.Sleep(1 * time.Second)
	fmt.Printf("=====> Application deployed:\n")
	fmt.Printf("       http://%s:%s\n", hostname, getPort(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
        out, err := exec.Command("bash", "-c", cmdline).Output()
        if err != nil {
                panic(err)
        }
        return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		return
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

func getPort(jobid string) string {
	job, err := host.GetJob(jobid)
	if err != nil {
		log.Fatal(err)
	}
	for portspec := range job.Job.Config.ExposedPorts {
		return strings.Split(portspec, "/", 1)[0]
	}
}

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {

	services, err := sd.Services("flynn-lorne-attach." + hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.Dial("tcp", services.OnlineAddrs()[0])
	if err != nil {
		log.Fatal(err)
	}
	err = gob.NewEncoder(conn).Encode(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	})
	if err != nil {
		log.Fatal(err)
	}
	attachState := make([]byte, 1)
	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}
	switch attachState[0] {
	case lorne.AttachError:
		log.Fatal("attach error")
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.(*net.TCPConn).CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<commit_msg>demo: wrong args<commit_after>package main

import (
	"os/exec"
	"strings"
	"encoding/gob"
	"fmt"
	"bufio"
	"io"
	"log"
	"net"
	"time"
	"os"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	"github.com/titanous/go-dockerclient"
	"github.com/flynn/sampi/types"
	sc "github.com/flynn/sampi/client"
	lc "github.com/flynn/lorne/client"
)

// WARNING: assumes one host at the moment (firstHost will always be the same)

var sd *discover.Client
var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sd, err = discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"
	hostname := shell("curl -s icanhazip.com")

	set, _ := sd.Services("shelf")
	addrs := set.OnlineAddrs()
	if len(addrs) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := addrs[0]

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s on %s ...\n", app, hostname)

	scheduleAndAttach(app + ".build", docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := app + ".web"

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start", "web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
		},
	})

	time.Sleep(1 * time.Second)
	fmt.Printf("=====> Application deployed:\n")
	fmt.Printf("       http://%s:%s\n", hostname, getPort(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
        out, err := exec.Command("bash", "-c", cmdline).Output()
        if err != nil {
                panic(err)
        }
        return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		return
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

func getPort(jobid string) string {
	job, err := host.GetJob(jobid)
	if err != nil {
		log.Fatal(err)
	}
	for portspec := range job.Job.Config.ExposedPorts {
		return strings.Split(portspec, "/")[0]
	}
}

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {

	services, err := sd.Services("flynn-lorne-attach." + hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.Dial("tcp", services.OnlineAddrs()[0])
	if err != nil {
		log.Fatal(err)
	}
	err = gob.NewEncoder(conn).Encode(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	})
	if err != nil {
		log.Fatal(err)
	}
	attachState := make([]byte, 1)
	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}
	switch attachState[0] {
	case lorne.AttachError:
		log.Fatal("attach error")
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.(*net.TCPConn).CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<|endoftext|>


<commit_before>package freexl

/*
#cgo linux pkg-config: libfreexl
#cgo darwin LDFLAGS: -lfreexl
#cgo windows LDFLAGS: -lfreexl
#include <stdlib.h>
#include <stdio.h>
#include <freexl.h>
// End Import

static int freexl_cell_get_type(FreeXL_CellValue cell) {
	return cell.type;
}

static int freexl_cell_get_int_value(FreeXL_CellValue cell) {
  return cell.value.int_value;
}

static double freexl_cell_get_double_value(FreeXL_CellValue cell) {
  return cell.value.double_value;
}

static const char * freexl_cell_get_text_value(FreeXL_CellValue cell) {
  return cell.value.text_value;
}
*/
import "C"

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"unsafe"
)

// Freexl handle
type Freexl struct {
	handle   unsafe.Pointer
	MaxSheet int
	Sheets   []Sheet
}

// Sheet handle
type Sheet struct {
	MaxRow int
	MaxCol int
	Name   string
	Values [][]string
}

// Open filepath
func Open(path string) (f *Freexl, err error) {
	f = &Freexl{}
	err = f.Open(path)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// OpenReader with temporary file
func OpenReader(r io.Reader) (f *Freexl, err error) {
	f = &Freexl{}
	tmp, err := ioutil.TempFile(os.TempDir(), "freexls")
	if err != nil {
		return nil, err
	}
	defer os.Remove(tmp.Name())

	_, err = io.Copy(tmp, r)
	if err != nil {
		return nil, err
	}

	err = f.Open(tmp.Name())
	if err != nil {
		return nil, err
	}

	return f, nil
}

// OpenBinary with temporary file
func OpenBinary(b []byte) (f *Freexl, err error) {
	return OpenReader(bytes.NewReader(b))
}

// Open file with defined handle
func (f *Freexl) Open(path string) error {
	pName := C.CString(path)
	defer C.free(unsafe.Pointer(pName))
	defer C.freexl_close(f.handle)

	ret := C.freexl_open(pName, &f.handle)
	if ret != OK {
		return fmt.Errorf("Freexl: error opening %s : %d", path, ret)
	}

	info, err := f.getInfo(BIFF_SHEET_COUNT)
	if err != nil {
		return err
	}

	f.MaxSheet = info
	f.Sheets = make([]Sheet, f.MaxSheet)

	for i, sheet := range f.Sheets {
		if err = sheet.getInfo(i, f.handle); err != nil {
			return err
		}
		f.Sheets[i] = sheet
	}

	return nil
}

func (f *Freexl) getInfo(t int) (int, error) {
	var info C.uint
	ret := C.freexl_get_info(f.handle, C.ushort(t), &info)
	if ret != OK {
		return 0, fmt.Errorf("Freexl: error get info %d: %d", t, ret)
	}

	return int(info), nil
}

func (s *Sheet) getInfo(i int, handle unsafe.Pointer) error {
	name := C.CString("")
	defer C.free(unsafe.Pointer(name))

	var numRow C.uint
	var numCol C.ushort

	ret := C.freexl_get_worksheet_name(handle, C.ushort(i), &name)
	if ret != OK {
		return fmt.Errorf("Freexl: error get sheet name :%d", ret)
	}

	s.Name = C.GoString(name)

	ret = C.freexl_select_active_worksheet(handle, C.ushort(i))
	if ret != OK {
		return fmt.Errorf("Freexl: failed to selct worksheet: %s", ret)
	}

	ret = C.freexl_worksheet_dimensions(handle, &numRow, &numCol)
	if ret != OK {
		return fmt.Errorf("Freexl: failed to get dimensions: %s", ret)
	}

	s.MaxRow = int(numRow) - 1
	s.MaxCol = int(numCol) - 1

	s.Values = make([][]string, s.MaxRow)

	for i, row := range s.Values {
		row = make([]string, s.MaxCol)
		var cell C.FreeXL_CellValue
		for j := range row {
			ret = C.freexl_get_cell_value(handle, C.uint(i), C.ushort(j), &cell)
			if ret != OK {
				continue
			}

			switch C.freexl_cell_get_type(cell) {
			case CELL_DOUBLE:
				row = append(row, fmt.Sprintf("%1.12f", float64(C.freexl_cell_get_double_value(cell))))
			case CELL_INT:
				row = append(row, fmt.Sprintf("%d", int(C.freexl_cell_get_int_value(cell))))
			case CELL_TEXT, CELL_SST_TEXT, CELL_DATE, CELL_DATETIME, CELL_TIME:
				row = append(row, C.GoString(C.freexl_cell_get_text_value(cell)))
			default:
				fallthrough
			case CELL_NULL:
				row = append(row, "")
			}
		}

		s.Values[i] = row
	}

	return nil
}
<commit_msg>FIX: canceled deducting the maxrow ``` it was the test file with extra row and column ```<commit_after>package freexl

/*
#cgo linux pkg-config: libfreexl
#cgo darwin LDFLAGS: -lfreexl
#cgo windows LDFLAGS: -lfreexl
#include <stdlib.h>
#include <stdio.h>
#include <freexl.h>
// End Import

static int freexl_cell_get_type(FreeXL_CellValue cell) {
	return cell.type;
}

static int freexl_cell_get_int_value(FreeXL_CellValue cell) {
  return cell.value.int_value;
}

static double freexl_cell_get_double_value(FreeXL_CellValue cell) {
  return cell.value.double_value;
}

static const char * freexl_cell_get_text_value(FreeXL_CellValue cell) {
  return cell.value.text_value;
}
*/
import "C"

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"unsafe"
)

// Freexl handle
type Freexl struct {
	handle   unsafe.Pointer
	MaxSheet int
	Sheets   []Sheet
}

// Sheet handle
type Sheet struct {
	MaxRow int
	MaxCol int
	Name   string
	Values [][]string
}

// Open filepath
func Open(path string) (f *Freexl, err error) {
	f = &Freexl{}
	err = f.Open(path)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// OpenReader with temporary file
func OpenReader(r io.Reader) (f *Freexl, err error) {
	f = &Freexl{}
	tmp, err := ioutil.TempFile(os.TempDir(), "freexls")
	if err != nil {
		return nil, err
	}
	defer os.Remove(tmp.Name())

	_, err = io.Copy(tmp, r)
	if err != nil {
		return nil, err
	}

	err = f.Open(tmp.Name())
	if err != nil {
		return nil, err
	}

	return f, nil
}

// OpenBinary with temporary file
func OpenBinary(b []byte) (f *Freexl, err error) {
	return OpenReader(bytes.NewReader(b))
}

// Open file with defined handle
func (f *Freexl) Open(path string) error {
	pName := C.CString(path)
	defer C.free(unsafe.Pointer(pName))
	defer C.freexl_close(f.handle)

	ret := C.freexl_open(pName, &f.handle)
	if ret != OK {
		return fmt.Errorf("Freexl: error opening %s : %d", path, ret)
	}

	info, err := f.getInfo(BIFF_SHEET_COUNT)
	if err != nil {
		return err
	}

	f.MaxSheet = info
	f.Sheets = make([]Sheet, f.MaxSheet)

	for i, sheet := range f.Sheets {
		if err = sheet.getInfo(i, f.handle); err != nil {
			return err
		}
		f.Sheets[i] = sheet
	}

	return nil
}

func (f *Freexl) getInfo(t int) (int, error) {
	var info C.uint
	ret := C.freexl_get_info(f.handle, C.ushort(t), &info)
	if ret != OK {
		return 0, fmt.Errorf("Freexl: error get info %d: %d", t, ret)
	}

	return int(info), nil
}

func (s *Sheet) getInfo(i int, handle unsafe.Pointer) error {
	name := C.CString("")
	defer C.free(unsafe.Pointer(name))

	var numRow C.uint
	var numCol C.ushort

	ret := C.freexl_get_worksheet_name(handle, C.ushort(i), &name)
	if ret != OK {
		return fmt.Errorf("Freexl: error get sheet name :%d", ret)
	}

	s.Name = C.GoString(name)

	ret = C.freexl_select_active_worksheet(handle, C.ushort(i))
	if ret != OK {
		return fmt.Errorf("Freexl: failed to selct worksheet: %s", ret)
	}

	ret = C.freexl_worksheet_dimensions(handle, &numRow, &numCol)
	if ret != OK {
		return fmt.Errorf("Freexl: failed to get dimensions: %s", ret)
	}

	s.MaxRow = int(numRow)
	s.MaxCol = int(numCol)

	s.Values = make([][]string, s.MaxRow)

	for i, row := range s.Values {
		row = make([]string, s.MaxCol)
		var cell C.FreeXL_CellValue
		for j := range row {
			ret = C.freexl_get_cell_value(handle, C.uint(i), C.ushort(j), &cell)
			if ret != OK {
				continue
			}

			switch C.freexl_cell_get_type(cell) {
			case CELL_DOUBLE:
				row = append(row, fmt.Sprintf("%1.12f", float64(C.freexl_cell_get_double_value(cell))))
			case CELL_INT:
				row = append(row, fmt.Sprintf("%d", int(C.freexl_cell_get_int_value(cell))))
			case CELL_TEXT, CELL_SST_TEXT, CELL_DATE, CELL_DATETIME, CELL_TIME:
				row = append(row, C.GoString(C.freexl_cell_get_text_value(cell)))
			default:
				fallthrough
			case CELL_NULL:
				row = append(row, "")
			}
		}

		s.Values[i] = row
	}

	return nil
}
<|endoftext|>


<commit_before>package fridge

import (
	"errors"
	"github.com/shomali11/fridge/item"
	"github.com/shomali11/xredis"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Refresh is when an item was restocked with a fresher one
	Refresh = "REFRESH"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	invalidDurationsError = "Invalid 'best by' and 'use by' durations"
)

// NewClient returns a client using an xredis client
func NewClient(xredisClient *xredis.Client, options ...ConfigOption) *Client {
	client := &Client{
		config:   newConfig(options...),
		itemDao:  item.NewDao(xredisClient),
		eventBus: newEventBus(),
	}
	return client
}

// Client fridge client
type Client struct {
	config   *Config
	itemDao  *item.Dao
	eventBus *EventBus
}

// Put an item
func (c *Client) Put(key string, value string, options ...item.ConfigOption) error {
	itemConfig := newItemConfig(c.config, options...)
	if itemConfig.BestBy > itemConfig.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.itemDao.SetConfig(key, itemConfig)
	if err != nil {
		return err
	}

	err = c.itemDao.Set(key, value, itemConfig.GetUseByInSeconds())
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...item.QueryConfigOption) (string, bool, error) {
	queryConfig := newQueryConfig(options...)
	restock := queryConfig.Restock

	itemConfig, found, err := c.itemDao.GetConfig(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		itemConfig = c.newDefaultItemConfig()
	}

	cachedValue, found, err := c.itemDao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		if itemConfig.Timestamp.IsZero() {
			go c.publish(key, NotFound)
		} else {
			go c.publish(key, Expired)
		}
		return c.restock(key, restock)
	}

	now := time.Now().UTC()
	if now.Before(itemConfig.Timestamp.Add(itemConfig.BestBy)) {
		go c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(itemConfig.Timestamp.Add(itemConfig.UseBy)) {
		go c.publish(key, Cold)
		go c.restockAndCompare(key, cachedValue, restock)
		return cachedValue, true, nil
	}

	go c.publish(key, Expired)
	return c.restockAndCompare(key, cachedValue, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.itemDao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.itemDao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	return c.itemDao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.eventBus.HandleEvent(handleEvent)
}

func (c *Client) publish(key string, eventType string) {
	c.eventBus.Publish(key, eventType)
}

func (c *Client) restockAndCompare(key string, cachedValue string, callback func() (string, error)) (string, bool, error) {
	newValue, found, err := c.restock(key, callback)
	if err != nil || !found {
		return empty, found, err
	}

	if newValue == cachedValue {
		go c.publish(key, Unchanged)
	}
	return newValue, true, nil
}

func (c *Client) restock(key string, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		go c.publish(key, OutOfStock)
		return empty, false, nil
	}

	result, err := callback()
	if err != nil {
		return empty, false, err
	}

	go c.publish(key, Refresh)

	err = c.Put(key, result)
	if err != nil {
		return empty, false, err
	}
	return result, true, nil
}

func (c *Client) newDefaultItemConfig() *item.Config {
	return &item.Config{BestBy: c.config.defaultBestBy, UseBy: c.config.defaultUseBy}
}

func newItemConfig(config *Config, options ...item.ConfigOption) *item.Config {
	itemConfig := &item.Config{BestBy: config.defaultBestBy, UseBy: config.defaultUseBy}
	for _, option := range options {
		option(itemConfig)
	}
	return itemConfig
}

func newQueryConfig(options ...item.QueryConfigOption) *item.QueryConfig {
	queryConfig := &item.QueryConfig{}
	for _, option := range options {
		option(queryConfig)
	}
	return queryConfig
}
<commit_msg>Improvements<commit_after>package fridge

import (
	"errors"
	"github.com/shomali11/fridge/item"
	"github.com/shomali11/xredis"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Refresh is when an item was restocked with a fresher one
	Refresh = "REFRESH"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	invalidDurationsError = "Invalid 'best by' and 'use by' durations"
)

// NewClient returns a client using an xredis client
func NewClient(xredisClient *xredis.Client, options ...ConfigOption) *Client {
	client := &Client{
		config:   newConfig(options...),
		itemDao:  item.NewDao(xredisClient),
		eventBus: newEventBus(),
	}
	return client
}

// Client fridge client
type Client struct {
	config   *Config
	itemDao  *item.Dao
	eventBus *EventBus
}

// Put an item
func (c *Client) Put(key string, value string, options ...item.ConfigOption) error {
	itemConfig := newItemConfig(c.config, options...)
	if itemConfig.BestBy > itemConfig.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.itemDao.SetConfig(key, itemConfig)
	if err != nil {
		return err
	}

	err = c.itemDao.Set(key, value, itemConfig.GetUseByInSeconds())
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...item.QueryConfigOption) (string, bool, error) {
	queryConfig := newQueryConfig(options...)
	restock := queryConfig.Restock

	itemConfig, found, err := c.itemDao.GetConfig(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		go c.publish(key, NotFound)
		return empty, false, err
	}

	cachedValue, found, err := c.itemDao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		go c.publish(key, Expired)
		return c.restockAndCompare(key, cachedValue, restock)
	}

	now := time.Now().UTC()
	if now.Before(itemConfig.Timestamp.Add(itemConfig.BestBy)) {
		go c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(itemConfig.Timestamp.Add(itemConfig.UseBy)) {
		go c.publish(key, Cold)
		go c.restockAndCompare(key, cachedValue, restock)
		return cachedValue, true, nil
	}

	go c.publish(key, Expired)
	return c.restockAndCompare(key, cachedValue, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.itemDao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.itemDao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	return c.itemDao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.eventBus.HandleEvent(handleEvent)
}

func (c *Client) publish(key string, eventType string) {
	c.eventBus.Publish(key, eventType)
}

func (c *Client) restockAndCompare(key string, cachedValue string, callback func() (string, error)) (string, bool, error) {
	newValue, found, err := c.restock(key, callback)
	if err != nil || !found {
		return empty, found, err
	}

	if newValue == cachedValue {
		go c.publish(key, Unchanged)
	}
	return newValue, true, nil
}

func (c *Client) restock(key string, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		go c.publish(key, OutOfStock)
		return empty, false, nil
	}

	result, err := callback()
	if err != nil {
		return empty, false, err
	}

	go c.publish(key, Refresh)

	bestBy, useBy := c.getDurations(key)
	err = c.Put(key, result, item.WithDurations(bestBy, useBy))
	if err != nil {
		return empty, false, err
	}
	return result, true, nil
}

func (c *Client) getDurations(key string) (time.Duration, time.Duration) {
	bestBy := c.config.defaultBestBy
	useBy := c.config.defaultUseBy

	itemConfig, found, err := c.itemDao.GetConfig(key)
	if found && err == nil {
		bestBy = itemConfig.BestBy
		useBy = itemConfig.UseBy
	}
	return bestBy, useBy
}

func newItemConfig(config *Config, options ...item.ConfigOption) *item.Config {
	itemConfig := &item.Config{BestBy: config.defaultBestBy, UseBy: config.defaultUseBy}
	for _, option := range options {
		option(itemConfig)
	}
	return itemConfig
}

func newQueryConfig(options ...item.QueryConfigOption) *item.QueryConfig {
	queryConfig := &item.QueryConfig{}
	for _, option := range options {
		option(queryConfig)
	}
	return queryConfig
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fs

import (
	"container/list"
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

const dirSeparator = '/'

// Implementation detail, do not touch.
//
// How long we cache the most recent listing for a particular directory from
// GCS before regarding it as stale.
//
// Intended to paper over performance issues caused by quick follow-up calls;
// for example when the fuse VFS performs a readdir followed quickly by a
// lookup for each child. The drawback is that this increases the time before a
// write by a foreign machine within a recently-listed directory will be seen
// locally.
//
// TODO(jacobsa): Set this according to real-world performance issues when the
// kernel does e.g. ReadDir followed by LookUp. Can probably be set quite
// small.
//
// TODO(jacobsa): Can this be moved to a decorator implementation of gcs.Bucket
// instead of living here?
var ListingCacheTTL = 10 * time.Second

// Implementation detail, do not touch.
//
// How long we remember that we took some action on the contents of a directory
// (linking or unlinking), and pretend the action is reflected in the listing
// even if it is not.
//
// Intended to paper over the fact that GCS doesn't offer list-your-own-writes
// consistency: it may be an arbitrarily long time before you see the creation
// or deletion of an object in a subsequent listing, and even if you see it in
// one listing you may not in the next. The drawback is that modifications to
// recently-modified directories by foreign machines will not be reflected
// locally for awhile.
//
// TODO(jacobsa): Set this according to information about listing staleness
// distributions from the GCS team.
//
// TODO(jacobsa): Can this be moved to a decorator implementation of gcs.Bucket
// instead of living here?
var ChildActionMemoryTTL = 5 * time.Minute

// See the childModifications field of dir.
type childModification struct {
	time time.Time
	name string

	// INVARIANT: nil or of type *file or *dir.
	node fusefs.Node
}

// A "directory" in GCS, defined by an object name prefix.
//
// For example, if the bucket contains objects "foo/bar" and "foo/baz", this
// implicitly defines the directory "foo/". No matter what the contents of the
// bucket, there is an implicit root directory "".
type dir struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	logger *log.Logger
	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	// INVARIANT: objectPrefix is "" (representing the root directory) or ends
	// with dirSeparator.
	objectPrefix string

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

	// TODO(jacobsa): Make sure to initialize this correctly.
	fixmemu syncutil.InvariantMutex

	// Our current best understanding of the contents of the directory in GCS,
	// formed by listing the bucket and then patching according to child addition
	// and removal records at the time, and patched since then by subsequent
	// additions and removals. May be nil if no listing has happened or the
	// listing expired (see below).
	//
	// The time after which this should be generated anew from a new listing is
	// also stored. This is set to the time at which the listing completed plus
	// ListingCacheTTL.
	//
	// INVARIANT: All nodes are of type *dir or *file.
	// INVARIANT: All nodes are indexed by names that agree with node contents.
	contents           map[string]fusefs.Node // GUARDED_BY(mu)
	contentsExpiration time.Time              // GUARDED_BY(mu)

	// A collection of children that have recently been added or removed locally
	// and the time at which it happened, ordered by the sequence in which it
	// happened. Elements M with M.node == nil are removals; all others are
	// additions.
	//
	// For a record M in this list with M's age less than ChildActionMemoryTTL,
	// any listing from the bucket should be augmented by pretending M just
	// happened.
	//
	// TODO(jacobsa): Make sure to test link followed by unlink, and unlink
	// followed by link.
	//
	// TODO(jacobsa): Make sure to test that these expire eventually, i.e. that
	// foreign overwrites, deletes, and recreates are reflected eventually.
	//
	// INVARIANT: All elements are of type childModification.
	// INVARIANT: Contains no duplicate names.
	// INVARIANT: For each M with M.node == nil, contents does not contain M.name.
	// INVARIANT: For each M with M.node != nil,
	//              contents == nil || contents[M.name] == M.node.
	childModifications list.List // GUARDED_BY(mu)

	// An index of childModifications by name.
	//
	// INVARIANT: For all names N in the map, the indexed modification has name N.
	// INVARIANT: Contains exactly the set of names in childModifications.
	childModificationsIndex map[string]*list.Element // GUARDED_BY(mu)
}

// Make sure dir implements the interfaces we think it does.
var (
	_ fusefs.Node               = &dir{}
	_ fusefs.NodeCreater        = &dir{}
	_ fusefs.NodeMknoder        = &dir{}
	_ fusefs.NodeStringLookuper = &dir{}

	_ fusefs.Handle             = &dir{}
	_ fusefs.HandleReadDirAller = &dir{}
)

func newDir(
	logger *log.Logger,
	clock timeutil.Clock,
	bucket gcs.Bucket,
	objectPrefix string) *dir {
	return &dir{
		logger:       logger,
		clock:        clock,
		bucket:       bucket,
		objectPrefix: objectPrefix,
	}
}

// Ensure that d.contents is fresh and usable. Must be called before using
// d.contents.
//
// TODO(jacobsa): If contents hasn't expired, return immediately. Otherwise
// list parasitically while holding the lock (why not, we can make this more
// subtle later if we must) and modify the result appropriately.
//
// EXCLUSIVE_LOCKS_REQUIRED(d.mu)
func (d *dir) ensureContents(ctx context.Context) error

func (d *dir) Attr() fuse.Attr {
	return fuse.Attr{
		// TODO(jacobsa): Expose ACLs from GCS?
		Mode: os.ModeDir | 0500,
	}
}

func (d *dir) ReadDirAll(ctx context.Context) (
	ents []fuse.Dirent, err error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	d.logger.Printf("ReadDirAll: [%s]/%s", d.bucket.Name(), d.objectPrefix)

	// Ensure that our cache of children has been initialized.
	if err := d.initChildren(ctx); err != nil {
		return nil, fmt.Errorf("d.initChildren: %v", err)
	}

	// Read out the contents of the cache.
	for name, node := range d.children {
		ent := fuse.Dirent{
			Name: name,
		}

		if _, ok := node.(*dir); ok {
			ent.Type = fuse.DT_Dir
		} else {
			ent.Type = fuse.DT_File
		}

		ents = append(ents, ent)
	}

	return
}

func (d *dir) Lookup(ctx context.Context, name string) (fusefs.Node, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	d.logger.Printf("Lookup: ([%s]/%s) %s", d.bucket.Name(), d.objectPrefix, name)

	// Ensure that our cache of children has been initialized.
	if err := d.initChildren(ctx); err != nil {
		return nil, fmt.Errorf("d.initChildren: %v", err)
	}

	// Find the object within the map.
	if n, ok := d.children[name]; ok {
		return n, nil
	}

	return nil, fuse.ENOENT
}

func (d *dir) Create(
	ctx context.Context,
	req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (
	fusefs.Node,
	fusefs.Handle,
	error) {
	// Tell fuse to use Mknod followed by Open, rather than re-implementing much
	// of Open here.
	return nil, nil, fuse.ENOSYS
}

func (d *dir) Mknod(
	ctx context.Context,
	req *fuse.MknodRequest) (fusefs.Node, error) {
	return nil, errors.New("TODO(jacobsa): Support Mknod.")
}
<commit_msg>Added a TODO.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fs

import (
	"container/list"
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

const dirSeparator = '/'

// Implementation detail, do not touch.
//
// How long we cache the most recent listing for a particular directory from
// GCS before regarding it as stale.
//
// Intended to paper over performance issues caused by quick follow-up calls;
// for example when the fuse VFS performs a readdir followed quickly by a
// lookup for each child. The drawback is that this increases the time before a
// write by a foreign machine within a recently-listed directory will be seen
// locally.
//
// TODO(jacobsa): Set this according to real-world performance issues when the
// kernel does e.g. ReadDir followed by LookUp. Can probably be set quite
// small.
//
// TODO(jacobsa): Can this be moved to a decorator implementation of gcs.Bucket
// instead of living here?
var ListingCacheTTL = 10 * time.Second

// Implementation detail, do not touch.
//
// How long we remember that we took some action on the contents of a directory
// (linking or unlinking), and pretend the action is reflected in the listing
// even if it is not.
//
// Intended to paper over the fact that GCS doesn't offer list-your-own-writes
// consistency: it may be an arbitrarily long time before you see the creation
// or deletion of an object in a subsequent listing, and even if you see it in
// one listing you may not in the next. The drawback is that modifications to
// recently-modified directories by foreign machines will not be reflected
// locally for awhile.
//
// TODO(jacobsa): Set this according to information about listing staleness
// distributions from the GCS team.
//
// TODO(jacobsa): Can this be moved to a decorator implementation of gcs.Bucket
// instead of living here?
var ChildActionMemoryTTL = 5 * time.Minute

// See the childModifications field of dir.
type childModification struct {
	time time.Time
	name string

	// INVARIANT: nil or of type *file or *dir.
	node fusefs.Node
}

// A "directory" in GCS, defined by an object name prefix.
//
// For example, if the bucket contains objects "foo/bar" and "foo/baz", this
// implicitly defines the directory "foo/". No matter what the contents of the
// bucket, there is an implicit root directory "".
type dir struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	logger *log.Logger
	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	// INVARIANT: objectPrefix is "" (representing the root directory) or ends
	// with dirSeparator.
	objectPrefix string

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

	// TODO(jacobsa): Make sure to initialize this correctly.
	fixmemu syncutil.InvariantMutex

	// Our current best understanding of the contents of the directory in GCS,
	// formed by listing the bucket and then patching according to child addition
	// and removal records at the time, and patched since then by subsequent
	// additions and removals. May be nil if no listing has happened or the
	// listing expired (see below).
	//
	// The time after which this should be generated anew from a new listing is
	// also stored. This is set to the time at which the listing completed plus
	// ListingCacheTTL.
	//
	// INVARIANT: All nodes are of type *dir or *file.
	// INVARIANT: All nodes are indexed by names that agree with node contents.
	contents           map[string]fusefs.Node // GUARDED_BY(mu)
	contentsExpiration time.Time              // GUARDED_BY(mu)

	// A collection of children that have recently been added or removed locally
	// and the time at which it happened, ordered by the sequence in which it
	// happened. Elements M with M.node == nil are removals; all others are
	// additions.
	//
	// For a record M in this list with M's age less than ChildActionMemoryTTL,
	// any listing from the bucket should be augmented by pretending M just
	// happened.
	//
	// TODO(jacobsa): Make sure to test link followed by unlink, and unlink
	// followed by link.
	//
	// TODO(jacobsa): Make sure to test that these expire eventually, i.e. that
	// foreign overwrites, deletes, and recreates are reflected eventually.
	//
	// INVARIANT: All elements are of type childModification.
	// INVARIANT: Contains no duplicate names.
	// INVARIANT: For each M with M.node == nil, contents does not contain M.name.
	// INVARIANT: For each M with M.node != nil,
	//              contents == nil || contents[M.name] == M.node.
	childModifications list.List // GUARDED_BY(mu)

	// An index of childModifications by name.
	//
	// INVARIANT: For all names N in the map, the indexed modification has name N.
	// INVARIANT: Contains exactly the set of names in childModifications.
	childModificationsIndex map[string]*list.Element // GUARDED_BY(mu)
}

// Make sure dir implements the interfaces we think it does.
var (
	_ fusefs.Node               = &dir{}
	_ fusefs.NodeCreater        = &dir{}
	_ fusefs.NodeMknoder        = &dir{}
	_ fusefs.NodeStringLookuper = &dir{}

	_ fusefs.Handle             = &dir{}
	_ fusefs.HandleReadDirAller = &dir{}
)

func newDir(
	logger *log.Logger,
	clock timeutil.Clock,
	bucket gcs.Bucket,
	objectPrefix string) *dir {
	return &dir{
		logger:       logger,
		clock:        clock,
		bucket:       bucket,
		objectPrefix: objectPrefix,
	}
}

// Ensure that d.contents is fresh and usable. Must be called before using
// d.contents.
//
// TODO(jacobsa): If contents hasn't expired, return immediately. Otherwise
// list parasitically while holding the lock (why not, we can make this more
// subtle later if we must) and modify the result appropriately.
//
// EXCLUSIVE_LOCKS_REQUIRED(d.mu)
func (d *dir) ensureContents(ctx context.Context) error

func (d *dir) Attr() fuse.Attr {
	return fuse.Attr{
		// TODO(jacobsa): Reflect that we allow writes now. Make sure to test.
		// TODO(jacobsa): Expose ACLs from GCS?
		Mode: os.ModeDir | 0500,
	}
}

func (d *dir) ReadDirAll(ctx context.Context) (
	ents []fuse.Dirent, err error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	d.logger.Printf("ReadDirAll: [%s]/%s", d.bucket.Name(), d.objectPrefix)

	// Ensure that our cache of children has been initialized.
	if err := d.initChildren(ctx); err != nil {
		return nil, fmt.Errorf("d.initChildren: %v", err)
	}

	// Read out the contents of the cache.
	for name, node := range d.children {
		ent := fuse.Dirent{
			Name: name,
		}

		if _, ok := node.(*dir); ok {
			ent.Type = fuse.DT_Dir
		} else {
			ent.Type = fuse.DT_File
		}

		ents = append(ents, ent)
	}

	return
}

func (d *dir) Lookup(ctx context.Context, name string) (fusefs.Node, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	d.logger.Printf("Lookup: ([%s]/%s) %s", d.bucket.Name(), d.objectPrefix, name)

	// Ensure that our cache of children has been initialized.
	if err := d.initChildren(ctx); err != nil {
		return nil, fmt.Errorf("d.initChildren: %v", err)
	}

	// Find the object within the map.
	if n, ok := d.children[name]; ok {
		return n, nil
	}

	return nil, fuse.ENOENT
}

func (d *dir) Create(
	ctx context.Context,
	req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (
	fusefs.Node,
	fusefs.Handle,
	error) {
	// Tell fuse to use Mknod followed by Open, rather than re-implementing much
	// of Open here.
	return nil, nil, fuse.ENOSYS
}

func (d *dir) Mknod(
	ctx context.Context,
	req *fuse.MknodRequest) (fusefs.Node, error) {
	return nil, errors.New("TODO(jacobsa): Support Mknod.")
}
<|endoftext|>


<commit_before>package handlers

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"golang.org/x/crypto/acme/autocert"

	gh "github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/event"
	"github.com/play-with-docker/play-with-docker/pwd"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/urfave/negroni"
)

var core pwd.PWDApi
var e event.EventApi

type HandlerExtender func(h *mux.Router)

func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
	core = c
	e = ev
}

func Register(extend HandlerExtender) {
	r := mux.NewRouter()
	corsRouter := mux.NewRouter()

	corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOrigins([]string{"*"}))

	// Specific routes
	r.HandleFunc("/ping", Ping).Methods("GET")
	corsRouter.HandleFunc("/instances/images", GetInstanceImages).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", GetSession).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", CloseSession).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/setup", SessionSetup).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances", NewInstance).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", FileUpload).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST")

	r.HandleFunc("/ooc", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/ooc.html")
	}).Methods("GET")
	r.HandleFunc("/503", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/503.html")
	}).Methods("GET")
	r.HandleFunc("/p/{sessionId}", Home).Methods("GET")
	r.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./www")))
	r.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/robots.txt")
	})
	r.HandleFunc("/sdk.js", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/sdk.js")
	})

	corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
	r.Handle("/metrics", promhttp.Handler())

	// Generic routes
	r.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/landing.html")
	}).Methods("GET")

	r.HandleFunc("/users/me", LoggedInUser).Methods("GET")
	r.HandleFunc("/users/{userId}", GetUser).Methods("GET")
	r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")

	corsRouter.HandleFunc("/", NewSession).Methods("POST")

	if extend != nil {
		extend(corsRouter)
	}

	n := negroni.Classic()
	r.PathPrefix("/").Handler(negroni.New(negroni.Wrap(corsHandler(corsRouter))))
	n.UseHandler(r)

	httpServer := http.Server{
		Addr:              "0.0.0.0:" + config.PortNumber,
		Handler:           n,
		IdleTimeout:       30 * time.Second,
		ReadHeaderTimeout: 5 * time.Second,
	}

	if config.UseLetsEncrypt {
		certManager := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(config.LetsEncryptDomains...),
			Cache:      autocert.DirCache(config.LetsEncryptCertsDir),
		}

		httpServer.TLSConfig = &tls.Config{
			GetCertificate: certManager.GetCertificate,
		}

		go func() {
			http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
				http.Redirect(rw, r, fmt.Sprintf("https://%s", r.Host), http.StatusMovedPermanently)
			})
			log.Println("Starting redirect server")
			log.Fatal(http.ListenAndServe(":3001", nil))
			log.Fatal(httpServer.ListenAndServe())
		}()

		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServeTLS("", ""))
	} else {
		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServe())
	}
}
<commit_msg>Make me endpoint accessible through CORS. (#207)<commit_after>package handlers

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"golang.org/x/crypto/acme/autocert"

	gh "github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/event"
	"github.com/play-with-docker/play-with-docker/pwd"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/urfave/negroni"
)

var core pwd.PWDApi
var e event.EventApi

type HandlerExtender func(h *mux.Router)

func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
	core = c
	e = ev
}

func Register(extend HandlerExtender) {
	r := mux.NewRouter()
	corsRouter := mux.NewRouter()

	corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOrigins([]string{"*"}))

	// Specific routes
	r.HandleFunc("/ping", Ping).Methods("GET")
	corsRouter.HandleFunc("/instances/images", GetInstanceImages).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", GetSession).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", CloseSession).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/setup", SessionSetup).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances", NewInstance).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", FileUpload).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST")

	r.HandleFunc("/ooc", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/ooc.html")
	}).Methods("GET")
	r.HandleFunc("/503", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/503.html")
	}).Methods("GET")
	r.HandleFunc("/p/{sessionId}", Home).Methods("GET")
	r.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./www")))
	r.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/robots.txt")
	})
	r.HandleFunc("/sdk.js", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/sdk.js")
	})

	corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
	r.Handle("/metrics", promhttp.Handler())

	// Generic routes
	r.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/landing.html")
	}).Methods("GET")

	corsRouter.HandleFunc("/users/me", LoggedInUser).Methods("GET")
	r.HandleFunc("/users/{userId:^(?me)}", GetUser).Methods("GET")
	r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")

	corsRouter.HandleFunc("/", NewSession).Methods("POST")

	if extend != nil {
		extend(corsRouter)
	}

	n := negroni.Classic()
	r.PathPrefix("/").Handler(negroni.New(negroni.Wrap(corsHandler(corsRouter))))
	n.UseHandler(r)

	httpServer := http.Server{
		Addr:              "0.0.0.0:" + config.PortNumber,
		Handler:           n,
		IdleTimeout:       30 * time.Second,
		ReadHeaderTimeout: 5 * time.Second,
	}

	if config.UseLetsEncrypt {
		certManager := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(config.LetsEncryptDomains...),
			Cache:      autocert.DirCache(config.LetsEncryptCertsDir),
		}

		httpServer.TLSConfig = &tls.Config{
			GetCertificate: certManager.GetCertificate,
		}

		go func() {
			http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
				http.Redirect(rw, r, fmt.Sprintf("https://%s", r.Host), http.StatusMovedPermanently)
			})
			log.Println("Starting redirect server")
			log.Fatal(http.ListenAndServe(":3001", nil))
			log.Fatal(httpServer.ListenAndServe())
		}()

		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServeTLS("", ""))
	} else {
		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServe())
	}
}
<|endoftext|>


<commit_before>package format

import (
	"bytes"
	"io"
	"strconv"

	"github.com/schemalex/schemalex/internal/errors"
	"github.com/schemalex/schemalex/internal/util"
	"github.com/schemalex/schemalex/model"
)

// SQL takes an arbitrary `model.*` object and formats it as SQL,
// writing its result to `dst`
func SQL(dst io.Writer, v interface{}) error {
	switch v.(type) {
	case model.ColumnType:
		return formatColumnType(dst, v.(model.ColumnType))
	case model.Database:
		return formatDatabase(dst, v.(model.Database))
	case model.Stmts:
		for _, s := range v.(model.Stmts) {
			if err := SQL(dst, s); err != nil {
				return err
			}
		}
		return nil
	case model.Table:
		return formatTable(dst, v.(model.Table))
	case model.TableColumn:
		return formatTableColumn(dst, v.(model.TableColumn))
	case model.TableOption:
		return formatTableOption(dst, v.(model.TableOption))
	case model.Index:
		return formatIndex(dst, v.(model.Index))
	case model.Reference:
		return formatReference(dst, v.(model.Reference))
	default:
		return errors.New("unsupported model type")
	}
}

func formatDatabase(dst io.Writer, d model.Database) error {
	var buf bytes.Buffer
	buf.WriteString("CREATE DATABASE")
	if d.IsIfNotExists() {
		buf.WriteString(" IF NOT EXISTS")
	}
	buf.WriteByte(' ')
	buf.WriteString(util.Backquote(d.Name()))
	buf.WriteByte(';')

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatTableOption(dst io.Writer, option model.TableOption) error {
	var buf bytes.Buffer
	buf.WriteString(option.Key())
	buf.WriteString(" = ")
	buf.WriteString(option.Value())

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatTable(dst io.Writer, table model.Table) error {
	var buf bytes.Buffer

	buf.WriteString("CREATE")
	if table.IsTemporary() {
		buf.WriteString(" TEMPORARY")
	}

	buf.WriteString(" TABLE")
	if table.IsIfNotExists() {
		buf.WriteString(" IF NOT EXISTS")
	}

	buf.WriteByte(' ')
	buf.WriteString(util.Backquote(table.Name()))
	buf.WriteString(" (")

	colch := table.Columns()
	idxch := table.Indexes()
	colchmax := len(colch)
	idxchmax := len(idxch)

	var i int
	for col := range colch {
		buf.WriteByte('\n')
		if err := formatTableColumn(&buf, col); err != nil {
			return err
		}
		if i < colchmax-1 || idxchmax > 0 {
			buf.WriteByte(',')
		}
		i++
	}

	i = 0
	for idx := range idxch {
		buf.WriteByte('\n')
		if err := formatIndex(&buf, idx); err != nil {
			return err
		}
		if i < idxchmax-1 {
			buf.WriteByte(',')
		}
		i++
	}

	buf.WriteString("\n)")

	optch := table.Options()
	if l := len(optch); l > 0 {
		buf.WriteByte(' ')
		var i int
		for option := range optch {
			if err := formatTableOption(&buf, option); err != nil {
				return err
			}

			if i < l-1 {
				buf.WriteString(", ")
			}
			i++
		}
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatColumnType(dst io.Writer, col model.ColumnType) error {
	if col <= model.ColumnTypeInvalid || col >= model.ColumnTypeMax {
		return errors.New(`invalid column type`)
	}

	if _, err := io.WriteString(dst, col.String()); err != nil {
		return err
	}
	return nil
}

func formatTableColumn(dst io.Writer, col model.TableColumn) error {
	var buf bytes.Buffer

	buf.WriteString(util.Backquote(col.Name()))
	buf.WriteByte(' ')

	if err := formatColumnType(&buf, col.Type()); err != nil {
		return err
	}

	if col.HasLength() {
		buf.WriteString(" (")
		l := col.Length()
		buf.WriteString(l.Length())
		if l.HasDecimal() {
			buf.WriteByte(',')
			buf.WriteString(l.Decimal())
		}
		buf.WriteByte(')')
	}

	if col.IsUnsigned() {
		buf.WriteString(" UNSIGNED")
	}

	if col.IsZeroFill() {
		buf.WriteString(" ZEROFILL")
	}

	if col.IsBinary() {
		buf.WriteString(" BINARY")
	}

	if col.HasCharacterSet() {
		buf.WriteString(" CHARACTER SET ")
		buf.WriteString(util.Backquote(col.CharacterSet()))
	}

	if col.HasCollation() {
		buf.WriteString(" COLLATE ")
		buf.WriteString(util.Backquote(col.Collation()))
	}

	if n := col.NullState(); n != model.NullStateNone {
		buf.WriteByte(' ')
		switch n {
		case model.NullStateNull:
			buf.WriteString("NULL")
		case model.NullStateNotNull:
			buf.WriteString("NOT NULL")
		}
	}

	if col.HasDefault() {
		buf.WriteString(" DEFAULT ")
		if col.IsQuotedDefault() {
			buf.WriteString(strconv.Quote(col.Default()))
		} else {
			buf.WriteString(col.Default())
		}
	}

	if col.IsAutoIncrement() {
		buf.WriteString(" AUTO_INCREMENT")
	}

	if col.IsUnique() {
		buf.WriteString(" UNIQUE KEY")
	}

	if col.IsPrimary() {
		buf.WriteString(" PRIMARY KEY")
	} else if col.IsKey() {
		buf.WriteString(" KEY")
	}

	if col.HasComment() {
		buf.WriteString(" '")
		buf.WriteString(col.Comment())
		buf.WriteByte('\'')
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatIndex(dst io.Writer, index model.Index) error {
	var buf bytes.Buffer

	if index.HasSymbol() {
		buf.WriteString("CONSTRAINT ")
		buf.WriteString(util.Backquote(index.Symbol()))
		buf.WriteByte(' ')
	}

	switch {
	case index.IsPrimaryKey():
		buf.WriteString("PRIMARY KEY")
	case index.IsNormal():
		buf.WriteString("INDEX")
	case index.IsUnique():
		buf.WriteString("UNIQUE INDEX")
	case index.IsFullText():
		buf.WriteString("FULLTEXT INDEX")
	case index.IsSpatial():
		buf.WriteString("SPATIAL INDEX")
	case index.IsForeginKey():
		buf.WriteString("FOREIGN KEY")
	}

	if index.HasName() {
		buf.WriteByte(' ')
		buf.WriteString(util.Backquote(index.Name()))
	}

	switch {
	case index.IsBtree():
		buf.WriteString(" USING BTREE")
	case index.IsHash():
		buf.WriteString(" USING HASH")
	}

	buf.WriteString(" (")
	ch := index.Columns()
	lch := len(ch)
	if lch == 0 {
		return errors.New(`no columns in index`)
	}

	var i int
	for col := range ch {
		buf.WriteString(util.Backquote(col))
		if i < lch-1 {
			buf.WriteString(", ")
		}
		i++
	}
	buf.WriteByte(')')

	if ref := index.Reference(); ref != nil {
		buf.WriteByte(' ')
		if err := formatReference(&buf, ref); err != nil {
			return err
		}
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatReference(dst io.Writer, r model.Reference) error {
	var buf bytes.Buffer

	buf.WriteString("REFERENCES ")
	buf.WriteString(util.Backquote(r.TableName()))
	buf.WriteString(" (")

	ch := r.Columns()
	lch := len(ch)
	var i int
	for col := range ch {
		buf.WriteString(util.Backquote(col))
		if i < lch-1 {
			buf.WriteString(", ")
		}
		i++
	}
	buf.WriteByte(')')

	switch {
	case r.MatchFull():
		buf.WriteString(" MATCH FULL")
	case r.MatchPartial():
		buf.WriteString(" MATCH PARTIAL")
	case r.MatchSimple():
		buf.WriteString(" MATCH SIMPLE")
	}

	// we should really check for errors...
	writeReferenceOption(&buf, "ON DELETE", r.OnDelete())
	writeReferenceOption(&buf, "ON UPDATE", r.OnUpdate())

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func writeReferenceOption(buf *bytes.Buffer, prefix string, opt model.ReferenceOption) error {
	if opt != model.ReferenceOptionNone {
		buf.WriteByte(' ')
		buf.WriteString(prefix)
		switch opt {
		case model.ReferenceOptionRestrict:
			buf.WriteString(" RESTRICT")
		case model.ReferenceOptionCascade:
			buf.WriteString(" CASCADE")
		case model.ReferenceOptionSetNull:
			buf.WriteString(" SET NULL")
		case model.ReferenceOptionNoAction:
			buf.WriteString(" NO ACTION")
		default:
			return errors.New("unknown reference option")
		}
	}
	return nil
}
<commit_msg>bugifix COMMENT not incleded<commit_after>package format

import (
	"bytes"
	"io"
	"strconv"

	"github.com/schemalex/schemalex/internal/errors"
	"github.com/schemalex/schemalex/internal/util"
	"github.com/schemalex/schemalex/model"
)

// SQL takes an arbitrary `model.*` object and formats it as SQL,
// writing its result to `dst`
func SQL(dst io.Writer, v interface{}) error {
	switch v.(type) {
	case model.ColumnType:
		return formatColumnType(dst, v.(model.ColumnType))
	case model.Database:
		return formatDatabase(dst, v.(model.Database))
	case model.Stmts:
		for _, s := range v.(model.Stmts) {
			if err := SQL(dst, s); err != nil {
				return err
			}
		}
		return nil
	case model.Table:
		return formatTable(dst, v.(model.Table))
	case model.TableColumn:
		return formatTableColumn(dst, v.(model.TableColumn))
	case model.TableOption:
		return formatTableOption(dst, v.(model.TableOption))
	case model.Index:
		return formatIndex(dst, v.(model.Index))
	case model.Reference:
		return formatReference(dst, v.(model.Reference))
	default:
		return errors.New("unsupported model type")
	}
}

func formatDatabase(dst io.Writer, d model.Database) error {
	var buf bytes.Buffer
	buf.WriteString("CREATE DATABASE")
	if d.IsIfNotExists() {
		buf.WriteString(" IF NOT EXISTS")
	}
	buf.WriteByte(' ')
	buf.WriteString(util.Backquote(d.Name()))
	buf.WriteByte(';')

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatTableOption(dst io.Writer, option model.TableOption) error {
	var buf bytes.Buffer
	buf.WriteString(option.Key())
	buf.WriteString(" = ")
	buf.WriteString(option.Value())

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatTable(dst io.Writer, table model.Table) error {
	var buf bytes.Buffer

	buf.WriteString("CREATE")
	if table.IsTemporary() {
		buf.WriteString(" TEMPORARY")
	}

	buf.WriteString(" TABLE")
	if table.IsIfNotExists() {
		buf.WriteString(" IF NOT EXISTS")
	}

	buf.WriteByte(' ')
	buf.WriteString(util.Backquote(table.Name()))
	buf.WriteString(" (")

	colch := table.Columns()
	idxch := table.Indexes()
	colchmax := len(colch)
	idxchmax := len(idxch)

	var i int
	for col := range colch {
		buf.WriteByte('\n')
		if err := formatTableColumn(&buf, col); err != nil {
			return err
		}
		if i < colchmax-1 || idxchmax > 0 {
			buf.WriteByte(',')
		}
		i++
	}

	i = 0
	for idx := range idxch {
		buf.WriteByte('\n')
		if err := formatIndex(&buf, idx); err != nil {
			return err
		}
		if i < idxchmax-1 {
			buf.WriteByte(',')
		}
		i++
	}

	buf.WriteString("\n)")

	optch := table.Options()
	if l := len(optch); l > 0 {
		buf.WriteByte(' ')
		var i int
		for option := range optch {
			if err := formatTableOption(&buf, option); err != nil {
				return err
			}

			if i < l-1 {
				buf.WriteString(", ")
			}
			i++
		}
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatColumnType(dst io.Writer, col model.ColumnType) error {
	if col <= model.ColumnTypeInvalid || col >= model.ColumnTypeMax {
		return errors.New(`invalid column type`)
	}

	if _, err := io.WriteString(dst, col.String()); err != nil {
		return err
	}
	return nil
}

func formatTableColumn(dst io.Writer, col model.TableColumn) error {
	var buf bytes.Buffer

	buf.WriteString(util.Backquote(col.Name()))
	buf.WriteByte(' ')

	if err := formatColumnType(&buf, col.Type()); err != nil {
		return err
	}

	if col.HasLength() {
		buf.WriteString(" (")
		l := col.Length()
		buf.WriteString(l.Length())
		if l.HasDecimal() {
			buf.WriteByte(',')
			buf.WriteString(l.Decimal())
		}
		buf.WriteByte(')')
	}

	if col.IsUnsigned() {
		buf.WriteString(" UNSIGNED")
	}

	if col.IsZeroFill() {
		buf.WriteString(" ZEROFILL")
	}

	if col.IsBinary() {
		buf.WriteString(" BINARY")
	}

	if col.HasCharacterSet() {
		buf.WriteString(" CHARACTER SET ")
		buf.WriteString(util.Backquote(col.CharacterSet()))
	}

	if col.HasCollation() {
		buf.WriteString(" COLLATE ")
		buf.WriteString(util.Backquote(col.Collation()))
	}

	if n := col.NullState(); n != model.NullStateNone {
		buf.WriteByte(' ')
		switch n {
		case model.NullStateNull:
			buf.WriteString("NULL")
		case model.NullStateNotNull:
			buf.WriteString("NOT NULL")
		}
	}

	if col.HasDefault() {
		buf.WriteString(" DEFAULT ")
		if col.IsQuotedDefault() {
			buf.WriteString(strconv.Quote(col.Default()))
		} else {
			buf.WriteString(col.Default())
		}
	}

	if col.IsAutoIncrement() {
		buf.WriteString(" AUTO_INCREMENT")
	}

	if col.IsUnique() {
		buf.WriteString(" UNIQUE KEY")
	}

	if col.IsPrimary() {
		buf.WriteString(" PRIMARY KEY")
	} else if col.IsKey() {
		buf.WriteString(" KEY")
	}

	if col.HasComment() {
		buf.WriteString(" COMMENT '")
		buf.WriteString(col.Comment())
		buf.WriteByte('\'')
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatIndex(dst io.Writer, index model.Index) error {
	var buf bytes.Buffer

	if index.HasSymbol() {
		buf.WriteString("CONSTRAINT ")
		buf.WriteString(util.Backquote(index.Symbol()))
		buf.WriteByte(' ')
	}

	switch {
	case index.IsPrimaryKey():
		buf.WriteString("PRIMARY KEY")
	case index.IsNormal():
		buf.WriteString("INDEX")
	case index.IsUnique():
		buf.WriteString("UNIQUE INDEX")
	case index.IsFullText():
		buf.WriteString("FULLTEXT INDEX")
	case index.IsSpatial():
		buf.WriteString("SPATIAL INDEX")
	case index.IsForeginKey():
		buf.WriteString("FOREIGN KEY")
	}

	if index.HasName() {
		buf.WriteByte(' ')
		buf.WriteString(util.Backquote(index.Name()))
	}

	switch {
	case index.IsBtree():
		buf.WriteString(" USING BTREE")
	case index.IsHash():
		buf.WriteString(" USING HASH")
	}

	buf.WriteString(" (")
	ch := index.Columns()
	lch := len(ch)
	if lch == 0 {
		return errors.New(`no columns in index`)
	}

	var i int
	for col := range ch {
		buf.WriteString(util.Backquote(col))
		if i < lch-1 {
			buf.WriteString(", ")
		}
		i++
	}
	buf.WriteByte(')')

	if ref := index.Reference(); ref != nil {
		buf.WriteByte(' ')
		if err := formatReference(&buf, ref); err != nil {
			return err
		}
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func formatReference(dst io.Writer, r model.Reference) error {
	var buf bytes.Buffer

	buf.WriteString("REFERENCES ")
	buf.WriteString(util.Backquote(r.TableName()))
	buf.WriteString(" (")

	ch := r.Columns()
	lch := len(ch)
	var i int
	for col := range ch {
		buf.WriteString(util.Backquote(col))
		if i < lch-1 {
			buf.WriteString(", ")
		}
		i++
	}
	buf.WriteByte(')')

	switch {
	case r.MatchFull():
		buf.WriteString(" MATCH FULL")
	case r.MatchPartial():
		buf.WriteString(" MATCH PARTIAL")
	case r.MatchSimple():
		buf.WriteString(" MATCH SIMPLE")
	}

	// we should really check for errors...
	writeReferenceOption(&buf, "ON DELETE", r.OnDelete())
	writeReferenceOption(&buf, "ON UPDATE", r.OnUpdate())

	if _, err := buf.WriteTo(dst); err != nil {
		return err
	}
	return nil
}

func writeReferenceOption(buf *bytes.Buffer, prefix string, opt model.ReferenceOption) error {
	if opt != model.ReferenceOptionNone {
		buf.WriteByte(' ')
		buf.WriteString(prefix)
		switch opt {
		case model.ReferenceOptionRestrict:
			buf.WriteString(" RESTRICT")
		case model.ReferenceOptionCascade:
			buf.WriteString(" CASCADE")
		case model.ReferenceOptionSetNull:
			buf.WriteString(" SET NULL")
		case model.ReferenceOptionNoAction:
			buf.WriteString(" NO ACTION")
		default:
			return errors.New("unknown reference option")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package shell

import (
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
	. "../ifdbg"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type Cmd struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnFork  func(*Cmd) error
	OffFork func(*Cmd) error
	OnClone func(*Cmd) error
	Closers []io.Closer
}

func (this *Cmd) GetRawArgs() []string {
	return this.RawArgs
}

func (this *Cmd) Close() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func New() *Cmd {
	this := Cmd{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	return &this
}

func (this *Cmd) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Cmd) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Cmd) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Cmd) Clone() (*Cmd, error) {
	rv := new(Cmd)
	rv.Args = this.Args
	rv.RawArgs = this.RawArgs
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = this.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	rv.OnFork = this.OnFork
	rv.OffFork = this.OffFork
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Cmd, args []string) ([]string, error)

var argsHook = func(it *Cmd, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(context.Context, *Cmd) (int, bool, error)

var hook = func(context.Context, *Cmd) (int, bool, error) {
	return 0, false, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Cmd, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var LastErrorLevel int

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Cmd) spawnvp_noerrmsg(ctx context.Context) (int, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return 0, nil
	}
	if DBG {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, done, err := hook(ctx, this); done || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path = dos.LookPath(this.Args[0], "NYAGOSPATH")
	if this.Path == "" {
		return 255, OnCommandNotFound(this, os.ErrNotExist)
	}
	this.Args[0] = this.Path
	if DBG {
		print("exec.LookPath(", this.Args[0], ")==", this.Path, "\n")
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return 0, err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return errorlevel, err
	} else {
		return 255, err
	}
}

type AlreadyReportedError struct {
	Err error
}

func (this AlreadyReportedError) Error() string {
	return ""
}

func IsAlreadyReported(err error) bool {
	_, ok := err.(AlreadyReportedError)
	return ok
}

func (this *Cmd) Spawnvp() (int, error) {
	return this.SpawnvpContext(context.Background())
}

func (this *Cmd) SpawnvpContext(ctx context.Context) (int, error) {
	errorlevel, err := this.spawnvp_noerrmsg(ctx)
	if err != nil && err != io.EOF && !IsAlreadyReported(err) {
		if DBG {
			val := reflect.ValueOf(err)
			fmt.Fprintf(this.Stderr, "error-type=%s\n", val.Type())
		}
		fmt.Fprintln(this.Stderr, err.Error())
		err = AlreadyReportedError{err}
	}
	return errorlevel, err
}

var pipeSeq uint = 0

func (this *Cmd) Interpret(text string) (int, error) {
	return this.InterpretContext(context.Background(), text)
}

func (this *Cmd) InterpretContext(ctx_ context.Context, text string) (errorlevel int, err error) {
	if DBG {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return 255, errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = 0
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if DBG {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return 0, statementsErr
	}
	if argsHook != nil {
		if DBG {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return 255, err
				}
			}
		}
		if DBG {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return 255, errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		shutdown_immediately := false
		for i, state := range pipeline {
			if DBG {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd := new(Cmd)
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround
			cmd.Tag = this.Tag
			cmd.HookCount = this.HookCount
			cmd.SetStdin(nvl(this.Stdio[0], os.Stdin))
			cmd.SetStdout(nvl(this.Stdio[1], os.Stdout))
			cmd.SetStderr(nvl(this.Stdio[2], os.Stderr))
			cmd.OnClone = this.OnClone

			ctx := context.WithValue(ctx_, "gotoeol", func() {
				shutdown_immediately = true
				gotoeol, ok := ctx_.Value("gotoeol").(func())
				if ok {
					gotoeol()
				}
			})
			ctx = context.WithValue(ctx, "errorlevel", LastErrorLevel)
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return 255, err
				}
			}

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return 0, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				// foreground execution.
				errorlevel, err = cmd.SpawnvpContext(ctx)
				LastErrorLevel = errorlevel
				cmd.Close()
			} else {
				// background
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Cmd) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					if cmd1.OnFork != nil {
						if err := cmd1.OnFork(cmd1); err != nil {
							fmt.Fprintln(cmd1.Stderr, err.Error())
							goto exit
						}
					}
					cmd1.SpawnvpContext(ctx)
					if cmd1.OffFork != nil {
						if err := cmd1.OffFork(cmd1); err != nil {
							fmt.Fprintln(cmd1.Stderr, err.Error())
							goto exit
						}
					}
				exit:
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if shutdown_immediately {
				return errorlevel, nil
			}
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<commit_msg>Fix: forgotten copying shell.Cmd.OnFork/OffFork on one place<commit_after>package shell

import (
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
	. "../ifdbg"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type Cmd struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnFork  func(*Cmd) error
	OffFork func(*Cmd) error
	OnClone func(*Cmd) error
	Closers []io.Closer
}

func (this *Cmd) GetRawArgs() []string {
	return this.RawArgs
}

func (this *Cmd) Close() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func New() *Cmd {
	this := Cmd{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	return &this
}

func (this *Cmd) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Cmd) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Cmd) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Cmd) Clone() (*Cmd, error) {
	rv := new(Cmd)
	rv.Args = this.Args
	rv.RawArgs = this.RawArgs
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = this.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	rv.OnFork = this.OnFork
	rv.OffFork = this.OffFork
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Cmd, args []string) ([]string, error)

var argsHook = func(it *Cmd, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(context.Context, *Cmd) (int, bool, error)

var hook = func(context.Context, *Cmd) (int, bool, error) {
	return 0, false, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Cmd, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var LastErrorLevel int

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Cmd) spawnvp_noerrmsg(ctx context.Context) (int, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return 0, nil
	}
	if DBG {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, done, err := hook(ctx, this); done || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path = dos.LookPath(this.Args[0], "NYAGOSPATH")
	if this.Path == "" {
		return 255, OnCommandNotFound(this, os.ErrNotExist)
	}
	this.Args[0] = this.Path
	if DBG {
		print("exec.LookPath(", this.Args[0], ")==", this.Path, "\n")
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return 0, err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return errorlevel, err
	} else {
		return 255, err
	}
}

type AlreadyReportedError struct {
	Err error
}

func (this AlreadyReportedError) Error() string {
	return ""
}

func IsAlreadyReported(err error) bool {
	_, ok := err.(AlreadyReportedError)
	return ok
}

func (this *Cmd) Spawnvp() (int, error) {
	return this.SpawnvpContext(context.Background())
}

func (this *Cmd) SpawnvpContext(ctx context.Context) (int, error) {
	errorlevel, err := this.spawnvp_noerrmsg(ctx)
	if err != nil && err != io.EOF && !IsAlreadyReported(err) {
		if DBG {
			val := reflect.ValueOf(err)
			fmt.Fprintf(this.Stderr, "error-type=%s\n", val.Type())
		}
		fmt.Fprintln(this.Stderr, err.Error())
		err = AlreadyReportedError{err}
	}
	return errorlevel, err
}

var pipeSeq uint = 0

func (this *Cmd) Interpret(text string) (int, error) {
	return this.InterpretContext(context.Background(), text)
}

func (this *Cmd) InterpretContext(ctx_ context.Context, text string) (errorlevel int, err error) {
	if DBG {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return 255, errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = 0
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if DBG {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return 0, statementsErr
	}
	if argsHook != nil {
		if DBG {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return 255, err
				}
			}
		}
		if DBG {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return 255, errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		shutdown_immediately := false
		for i, state := range pipeline {
			if DBG {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd, err := this.Clone()
			if err != nil {
				return 255, err
			}
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround

			ctx := context.WithValue(ctx_, "gotoeol", func() {
				shutdown_immediately = true
				gotoeol, ok := ctx_.Value("gotoeol").(func())
				if ok {
					gotoeol()
				}
			})
			ctx = context.WithValue(ctx, "errorlevel", LastErrorLevel)
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return 255, err
				}
			}

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return 0, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				// foreground execution.
				errorlevel, err = cmd.SpawnvpContext(ctx)
				LastErrorLevel = errorlevel
				cmd.Close()
			} else {
				// background
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Cmd) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					if cmd1.OnFork != nil {
						if err := cmd1.OnFork(cmd1); err != nil {
							fmt.Fprintln(cmd1.Stderr, err.Error())
							goto exit
						}
					}
					cmd1.SpawnvpContext(ctx)
					if cmd1.OffFork != nil {
						if err := cmd1.OffFork(cmd1); err != nil {
							fmt.Fprintln(cmd1.Stderr, err.Error())
							goto exit
						}
					}
				exit:
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if shutdown_immediately {
				return errorlevel, nil
			}
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<|endoftext|>