<commit_before>package main

import (
	"../nsq"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"sync"
)

// DiskQueue implements the nsq.BackendQueue interface
// providing a filesystem backed FIFO queue
type DiskQueue struct {
	// separate mutexes are used to be able to exclusively
	// read/write or update meta-data
	metaMutex  sync.Mutex
	readMutex  sync.Mutex
	writeMutex sync.Mutex

	// instatiation time meta-data
	name            string
	dataPath        string
	maxBytesPerFile int64 // currently this cannot change once created
	syncEvery       int64 // number of writes per sync

	// run-time state (also persisted to disk)
	readPos      int64
	writePos     int64
	readFileNum  int64
	writeFileNum int64
	depth        int64

	// keeps track of the position where we have read
	// (but not yet sent over readChan)
	nextReadPos int64

	readFile  *os.File
	writeFile *os.File
	open      bool

	// exposed via ReadChan()
	readChan chan []byte

	// internal channels
	writeContinueChan chan int
	exitChan          chan int
}

// NewDiskQueue instantiates a new instance of DiskQueue, retrieving meta-data
// from the filesystem and starting the read ahead goroutine
func NewDiskQueue(name string, dataPath string, maxBytesPerFile int64, syncEvery int64) nsq.BackendQueue {
	d := DiskQueue{
		name:              name,
		dataPath:          dataPath,
		maxBytesPerFile:   maxBytesPerFile,
		readChan:          make(chan []byte),
		exitChan:          make(chan int),
		writeContinueChan: make(chan int),
		open:              true,
		syncEvery:         syncEvery,
	}

	// no need to lock here, nothing else could possibly be touching this instance
	err := d.retrieveMetaData()
	if err != nil {
		log.Printf("WARNING: diskqueue(%s) failed to retrieveMetaData - %s", d.name, err.Error())
	}

	go d.readAheadPump()

	return &d
}

// Depth returns the depth of the queue
func (d *DiskQueue) Depth() int64 {
	return d.depth
}

// ReadChan returns the []byte channel for reading data
func (d *DiskQueue) ReadChan() chan []byte {
	return d.readChan
}

// Put writes a single []byte to the queue
func (d *DiskQueue) Put(p []byte) error {
	return d.writeOne(p)
}

// Close cleans up the queue and persists meta-data
func (d *DiskQueue) Close() error {
	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	// this guarantees that no other goroutines can successfully read/write that may
	// currently be waiting on either of the above mutex
	d.open = false
	close(d.exitChan)

	if d.readFile != nil {
		d.readFile.Close()
	}

	if d.writeFile != nil {
		d.writeFile.Close()
	}

	err := d.persistMetaData()
	if err != nil {
		return err
	}

	return nil
}

// Empty destructively clears out any pending data in the queue
// by fast forwarding read positions and removing intermediate files
func (d *DiskQueue) Empty() error {
	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	d.metaMutex.Lock()
	for d.readFileNum < d.writeFileNum {
		fn := d.fileName(d.readFileNum)
		err := os.Remove(fn)
		if err != nil {
			// only log this state because, although unexpected,
			// it's effectively the same as having removed the file
			log.Printf("ERROR: failed to Remove(%s) - %s", fn, err.Error())
		}
		d.readFileNum++
	}
	d.readPos = d.writePos
	d.nextReadPos = d.readPos
	d.depth = 0
	d.metaMutex.Unlock()

	d.writeContinueChan <- 1

	return nil
}

// readOne performs a low level filesystem read for a single []byte
// while advancing read positions and rolling files, if necessary
func (d *DiskQueue) readOne() ([]byte, error) {
	var err error
	var msgSize int32

	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	if !d.open {
		return nil, errors.New("E_CLOSED")
	}

	if d.readPos > d.maxBytesPerFile {
		if d.readFile != nil {
			d.readFile.Close()
			d.readFile = nil
		}

		// get the current filename before incr
		fn := d.fileName(d.readFileNum)

		d.metaMutex.Lock()
		d.readFileNum++
		d.readPos = 0
		d.metaMutex.Unlock()
		err = d.persistMetaData()
		if err != nil {
			return nil, err
		}

		err := os.Remove(fn)
		if err != nil {
			log.Printf("ERROR: failed to Remove(%s) - %s", fn, err.Error())
		}
	}

	if d.readFile == nil {
		curFileName := d.fileName(d.readFileNum)
		d.readFile, err = os.OpenFile(curFileName, os.O_RDONLY, 0600)
		if err != nil {
			return nil, err
		}

		if d.readPos > 0 {
			_, err = d.readFile.Seek(d.readPos, 0)
			if err != nil {
				d.readFile.Close()
				d.readFile = nil
				return nil, err
			}
		}
	}

	err = binary.Read(d.readFile, binary.BigEndian, &msgSize)
	if err != nil {
		d.readFile.Close()
		d.readFile = nil
		return nil, err
	}

	readBuf := make([]byte, msgSize)
	_, err = io.ReadFull(d.readFile, readBuf)
	if err != nil {
		d.readFile.Close()
		d.readFile = nil
		return nil, err
	}

	totalBytes := 4 + msgSize
	// we only advance nextReadPos because we have not yet sent
	// this to consumers of the queue (where readPos will actually
	// be advanced)
	d.nextReadPos = d.readPos + int64(totalBytes)

	// log.Printf("DISK: read %d bytes - readFileNum=%d writeFileNum=%d readPos=%d writePos=%d\n",
	// 	totalBytes, d.readFileNum, d.writeFileNum, d.readPos, d.writePos)

	return readBuf, nil
}

// writeOne performs a low level filesystem write for a single []byte
// while advancing write positions and rolling files, if necessary
func (d *DiskQueue) writeOne(data []byte) error {
	var err error
	var buf bytes.Buffer

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	if !d.open {
		return errors.New("E_CLOSED")
	}

	if d.writePos > d.maxBytesPerFile {
		if d.writeFile != nil {
			d.writeFile.Close()
			d.writeFile = nil
		}

		d.metaMutex.Lock()
		d.writeFileNum++
		d.writePos = 0
		d.metaMutex.Unlock()
		err = d.persistMetaData()
		if err != nil {
			return err
		}
	}

	if d.writeFile == nil {
		curFileName := d.fileName(d.writeFileNum)
		d.writeFile, err = os.OpenFile(curFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}

		if d.writePos > 0 {
			_, err = d.writeFile.Seek(d.writePos, 0)
			if err != nil {
				d.writeFile.Close()
				d.writeFile = nil
				return err
			}
		}
	}

	dataLen := len(data)

	err = binary.Write(&buf, binary.BigEndian, int32(dataLen))
	if err != nil {
		return err
	}

	_, err = buf.Write(data)
	if err != nil {
		return err
	}

	_, err = d.writeFile.Write(buf.Bytes())
	if err != nil {
		d.writeFile.Close()
		d.writeFile = nil
		return err
	}

	if d.depth % d.syncEvery == 0 {
		err = d.writeFile.Sync()
		if err != nil {
			d.writeFile.Close()
			d.writeFile = nil
			return err
		}
	}

	totalBytes := 4 + dataLen
	d.metaMutex.Lock()
	d.writePos += int64(totalBytes)
	d.depth++
	d.metaMutex.Unlock()

	d.writeContinueChan <- 1

	// log.Printf("DISK: wrote %d bytes - readFileNum=%d writeFileNum=%d readPos=%d writePos=%d\n",
	// 	totalBytes, d.readFileNum, d.writeFileNum, d.readPos, d.writePos)

	return nil
}

// retrieveMetaData initializes state from the filesystem
func (d *DiskQueue) retrieveMetaData() error {
	var f *os.File
	var err error

	d.metaMutex.Lock()
	defer d.metaMutex.Unlock()

	fileName := d.metaDataFileName()
	f, err = os.OpenFile(fileName, os.O_RDONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = fmt.Fscanf(f, "%d\n%d,%d\n%d,%d\n",
		&d.depth,
		&d.readFileNum, &d.readPos,
		&d.writeFileNum, &d.writePos)
	if err != nil {
		return err
	}
	d.nextReadPos = d.readPos

	return nil
}

// persistMetaData atomically writes state to the filesystem
func (d *DiskQueue) persistMetaData() error {
	var f *os.File
	var err error

	d.metaMutex.Lock()
	defer d.metaMutex.Unlock()

	fileName := d.metaDataFileName()
	tmpFileName := fileName + ".tmp"

	// write to tmp file
	f, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintf(f, "%d\n%d,%d\n%d,%d\n",
		d.depth,
		d.readFileNum, d.readPos,
		d.writeFileNum, d.writePos)
	if err != nil {
		f.Close()
		return err
	}
	f.Sync()
	f.Close()

	// atomically rename
	return os.Rename(tmpFileName, fileName)
}

func (d *DiskQueue) metaDataFileName() string {
	return fmt.Sprintf(path.Join(d.dataPath, "%s.diskqueue.meta.dat"), d.name)
}

func (d *DiskQueue) fileName(fileNum int64) string {
	return fmt.Sprintf(path.Join(d.dataPath, "%s.diskqueue.%06d.dat"), d.name, fileNum)
}

func (d *DiskQueue) hasDataToRead() bool {
	return (d.readFileNum < d.writeFileNum) || (d.readPos < d.writePos)
}

// readAheadPump provides the backend for exposing a go channel (via ReadChan())
// in support of multiple concurrent queue consumers
//
// it works by looping and branching based on whether or not the queue has data
// to read and blocking until data is either read or written over the appropriate
// go channels
//
// conveniently this also means that we're asynchronously reading from the filesystem
func (d *DiskQueue) readAheadPump() {
	var data []byte
	var err error
	for {
		if d.hasDataToRead() {
			if d.nextReadPos == d.readPos {
				data, err = d.readOne()
				if err != nil {
					if err.Error() == "E_CLOSED" {
						return
					}
					log.Printf("ERROR: reading from diskqueue(%s) at %d of %s - %s",
						d.name, d.readPos, d.fileName(d.readFileNum), err.Error())
					// we assume that all read errors are recoverable...
					// it will probably turn out that this is a terrible assumption
					// as this could certainly result in an infinite busy loop
					continue
				}
			}
			select {
			case d.readChan <- data:
				d.metaMutex.Lock()
				d.depth--
				d.readPos = d.nextReadPos
				d.metaMutex.Unlock()
			case <-d.writeContinueChan:
			case <-d.exitChan:
				return
			}
		} else {
			select {
			case <-d.writeContinueChan:
			case <-d.exitChan:
				return
			}
		}
	}
}
<commit_msg>refactor thread safety for diskqueue<commit_after>package main

import (
	"../nsq"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"sync"
	"sync/atomic"
)

// DiskQueue implements the nsq.BackendQueue interface
// providing a filesystem backed FIFO queue
type DiskQueue struct {
	// separate mutexes are used to be able to exclusively
	// read/write or update meta-data
	metaMutex  sync.Mutex
	readMutex  sync.Mutex
	writeMutex sync.Mutex

	// instatiation time meta-data
	name            string
	dataPath        string
	maxBytesPerFile int64 // currently this cannot change once created
	syncEvery       int64 // number of writes per sync

	// run-time state (also persisted to disk)
	readPos      int64
	writePos     int64
	readFileNum  int64
	writeFileNum int64
	depth        int64

	// keeps track of the position where we have read
	// (but not yet sent over readChan)
	nextReadPos     int64
	nextReadFileNum int64

	readFile  *os.File
	writeFile *os.File
	open      bool

	// exposed via ReadChan()
	readChan chan []byte

	// internal channels
	emptyChan         chan int
	writeContinueChan chan int
	exitChan          chan int
}

// NewDiskQueue instantiates a new instance of DiskQueue, retrieving meta-data
// from the filesystem and starting the read ahead goroutine
func NewDiskQueue(name string, dataPath string, maxBytesPerFile int64, syncEvery int64) nsq.BackendQueue {
	d := DiskQueue{
		name:              name,
		dataPath:          dataPath,
		maxBytesPerFile:   maxBytesPerFile,
		readChan:          make(chan []byte),
		emptyChan:         make(chan int),
		exitChan:          make(chan int),
		writeContinueChan: make(chan int),
		open:              true,
		syncEvery:         syncEvery,
	}

	// no need to lock here, nothing else could possibly be touching this instance
	err := d.retrieveMetaData()
	if err != nil {
		log.Printf("WARNING: diskqueue(%s) failed to retrieveMetaData - %s", d.name, err.Error())
	}

	go d.readAheadPump()

	return &d
}

// Depth returns the depth of the queue
func (d *DiskQueue) Depth() int64 {
	return atomic.LoadInt64(&d.depth)
}

// ReadChan returns the []byte channel for reading data
func (d *DiskQueue) ReadChan() chan []byte {
	return d.readChan
}

// Put writes a single []byte to the queue
func (d *DiskQueue) Put(p []byte) error {
	return d.writeOne(p)
}

// Close cleans up the queue and persists meta-data
func (d *DiskQueue) Close() error {
	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	// this guarantees that no other goroutines can successfully read/write that may
	// currently be waiting on either of the above mutex
	d.open = false
	close(d.exitChan)

	if d.readFile != nil {
		d.readFile.Close()
	}

	if d.writeFile != nil {
		d.writeFile.Close()
	}

	err := d.persistMetaData()
	if err != nil {
		return err
	}

	return nil
}

// Empty destructively clears out any pending data in the queue
// by fast forwarding read positions and removing intermediate files
func (d *DiskQueue) Empty() error {
	d.emptyChan <- 1
	return nil
}

func (d *DiskQueue) doEmpty() error {
	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	readFileNum := atomic.LoadInt64(&d.readFileNum)
	writeFileNum := atomic.LoadInt64(&d.writeFileNum)
	writePos := atomic.LoadInt64(&d.writePos)

	// make a list of read file numbers to remove (later)
	numsToRemove := make([]int64, 0)
	for readFileNum < writeFileNum {
		numsToRemove = append(numsToRemove, readFileNum)
		readFileNum++
	}

	atomic.StoreInt64(&d.readFileNum, writeFileNum)
	atomic.StoreInt64(&d.readPos, writePos)
	atomic.StoreInt64(&d.nextReadPos, writePos)
	atomic.StoreInt64(&d.depth, 0)

	err := d.persistMetaData()
	if err != nil {
		return err
	}

	// only if we've successfully persisted metadata do we remove old files
	for _, num := range numsToRemove {
		fn := d.fileName(num)
		err := os.Remove(fn)
		if err != nil {
			return err
		}
	}

	return nil
}

// readOne performs a low level filesystem read for a single []byte
// while advancing read positions and rolling files, if necessary
func (d *DiskQueue) readOne() ([]byte, error) {
	var err error
	var msgSize int32

	d.readMutex.Lock()
	defer d.readMutex.Unlock()

	if !d.open {
		return nil, errors.New("E_CLOSED")
	}

	// because readOne is only called inside readAheadPump
	// we do not need to lock around these
	readPos := atomic.LoadInt64(&d.readPos)
	readFileNum := atomic.LoadInt64(&d.readFileNum)

	if readPos > d.maxBytesPerFile {
		if d.readFile != nil {
			d.readFile.Close()
			d.readFile = nil
		}

		// get the current filename before incr
		fn := d.fileName(readFileNum)

		readFileNum++
		readPos = 0

		err = d.persistMetaData()
		if err != nil {
			return nil, err
		}

		// only if we've successfully persisted metadata do we remove old files
		err := os.Remove(fn)
		if err != nil {
			log.Printf("ERROR: failed to Remove(%s) - %s", fn, err.Error())
		}
	}

	if d.readFile == nil {
		curFileName := d.fileName(readFileNum)
		d.readFile, err = os.OpenFile(curFileName, os.O_RDONLY, 0600)
		if err != nil {
			return nil, err
		}

		log.Printf("DISKQUEUE: readOne() opened %s", curFileName)

		if readPos > 0 {
			_, err = d.readFile.Seek(readPos, 0)
			if err != nil {
				d.readFile.Close()
				d.readFile = nil
				return nil, err
			}
		}
	}

	err = binary.Read(d.readFile, binary.BigEndian, &msgSize)
	if err != nil {
		d.readFile.Close()
		d.readFile = nil
		return nil, err
	}

	readBuf := make([]byte, msgSize)
	_, err = io.ReadFull(d.readFile, readBuf)
	if err != nil {
		d.readFile.Close()
		d.readFile = nil
		return nil, err
	}

	totalBytes := int64(4 + msgSize)

	// we only advance next* because we have not yet sent this to consumers
	// (where readFileNum, readPos will actually be advanced)
	atomic.StoreInt64(&d.nextReadFileNum, readFileNum)
	atomic.StoreInt64(&d.nextReadPos, d.readPos+totalBytes)

	return readBuf, nil
}

// writeOne performs a low level filesystem write for a single []byte
// while advancing write positions and rolling files, if necessary
func (d *DiskQueue) writeOne(data []byte) error {
	var err error
	var buf bytes.Buffer

	d.writeMutex.Lock()
	defer d.writeMutex.Unlock()

	if !d.open {
		return errors.New("E_CLOSED")
	}

	writePos := atomic.LoadInt64(&d.writePos)
	writeFileNum := atomic.LoadInt64(&d.writeFileNum)

	if writePos > d.maxBytesPerFile {
		if d.writeFile != nil {
			d.writeFile.Close()
			d.writeFile = nil
		}

		writeFileNum++
		writePos = 0

		err = d.persistMetaData()
		if err != nil {
			return err
		}
	}

	if d.writeFile == nil {
		curFileName := d.fileName(writeFileNum)
		d.writeFile, err = os.OpenFile(curFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}

		log.Printf("DISKQUEUE: writeOne() opened %s", curFileName)

		if writePos > 0 {
			_, err = d.writeFile.Seek(writePos, 0)
			if err != nil {
				d.writeFile.Close()
				d.writeFile = nil
				return err
			}
		}
	}

	dataLen := len(data)

	err = binary.Write(&buf, binary.BigEndian, int32(dataLen))
	if err != nil {
		return err
	}

	_, err = buf.Write(data)
	if err != nil {
		return err
	}

	// only write to the file once
	_, err = d.writeFile.Write(buf.Bytes())
	if err != nil {
		d.writeFile.Close()
		d.writeFile = nil
		return err
	}

	// dont sync all the time :)
	if d.depth%d.syncEvery == 0 {
		err = d.writeFile.Sync()
		if err != nil {
			d.writeFile.Close()
			d.writeFile = nil
			return err
		}
	}

	writePos += int64(4 + dataLen)

	d.metaMutex.Lock()
	atomic.StoreInt64(&d.writeFileNum, writeFileNum)
	atomic.StoreInt64(&d.writePos, writePos)
	atomic.AddInt64(&d.depth, 1)
	d.metaMutex.Unlock()

	d.writeContinueChan <- 1

	return nil
}

// retrieveMetaData initializes state from the filesystem
func (d *DiskQueue) retrieveMetaData() error {
	var f *os.File
	var err error

	fileName := d.metaDataFileName()
	f, err = os.OpenFile(fileName, os.O_RDONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = fmt.Fscanf(f, "%d\n%d,%d\n%d,%d\n",
		&d.depth,
		&d.readFileNum, &d.readPos,
		&d.writeFileNum, &d.writePos)
	if err != nil {
		return err
	}
	d.nextReadFileNum = d.readFileNum
	d.nextReadPos = d.readPos

	return nil
}

// persistMetaData atomically writes state to the filesystem
func (d *DiskQueue) persistMetaData() error {
	var f *os.File
	var err error

	d.metaMutex.Lock()
	depth := atomic.LoadInt64(&d.depth)
	readFileNum := atomic.LoadInt64(&d.readFileNum)
	readPos := atomic.LoadInt64(&d.readPos)
	writeFileNum := atomic.LoadInt64(&d.writeFileNum)
	writePos := atomic.LoadInt64(&d.writePos)
	d.metaMutex.Unlock()

	fileName := d.metaDataFileName()
	tmpFileName := fileName + ".tmp"

	// write to tmp file
	f, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintf(f, "%d\n%d,%d\n%d,%d\n",
		depth,
		readFileNum, readPos,
		writeFileNum, writePos)
	if err != nil {
		f.Close()
		return err
	}
	f.Sync()
	f.Close()

	// atomically rename
	return os.Rename(tmpFileName, fileName)
}

func (d *DiskQueue) metaDataFileName() string {
	return fmt.Sprintf(path.Join(d.dataPath, "%s.diskqueue.meta.dat"), d.name)
}

func (d *DiskQueue) fileName(fileNum int64) string {
	return fmt.Sprintf(path.Join(d.dataPath, "%s.diskqueue.%06d.dat"), d.name, fileNum)
}

// readAheadPump provides the backend for exposing a go channel (via ReadChan())
// in support of multiple concurrent queue consumers
//
// it works by looping and branching based on whether or not the queue has data
// to read and blocking until data is either read or written over the appropriate
// go channels
//
// conveniently this also means that we're asynchronously reading from the filesystem
func (d *DiskQueue) readAheadPump() {
	var data []byte
	var err error
	for {
		nextReadPos := atomic.LoadInt64(&d.nextReadPos)

		readFileNum := atomic.LoadInt64(&d.readFileNum)
		readPos := atomic.LoadInt64(&d.readPos)

		d.metaMutex.Lock()
		writeFileNum := atomic.LoadInt64(&d.writeFileNum)
		writePos := atomic.LoadInt64(&d.writePos)
		d.metaMutex.Unlock()

		if (readFileNum < writeFileNum) || (readPos < writePos) {
			if nextReadPos == readPos {
				data, err = d.readOne()
				if err != nil {
					if err.Error() == "E_CLOSED" {
						return
					}
					log.Printf("ERROR: reading from diskqueue(%s) at %d of %s - %s",
						d.name, readPos, d.fileName(readFileNum), err.Error())
					// we assume that all read errors are recoverable...
					// it will probably turn out that this is a terrible assumption
					// as this could certainly result in an infinite busy loop
					continue
				}
			}

			select {
			case d.readChan <- data:
				d.metaMutex.Lock()
				atomic.AddInt64(&d.depth, -1)
				atomic.StoreInt64(&d.readFileNum, atomic.LoadInt64(&d.nextReadFileNum))
				atomic.StoreInt64(&d.readPos, atomic.LoadInt64(&d.nextReadPos))
				d.metaMutex.Unlock()
			case <-d.emptyChan:
				err := d.doEmpty()
				if err != nil {
					log.Printf("ERROR: doEmpty() - %s", err.Error())
				}
			case <-d.writeContinueChan:
			case <-d.exitChan:
				return
			}
		} else {
			select {
			case <-d.emptyChan:
				err := d.doEmpty()
				if err != nil {
					log.Printf("ERROR: doEmpty() - %s", err.Error())
				}
			case <-d.writeContinueChan:
			case <-d.exitChan:
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/bitly/nsq/nsq"
	"github.com/bmizerany/assert"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestStartup(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	iterations := 300
	doneExitChan := make(chan int)

	options := NewNsqdOptions()
	options.memQueueSize = 100
	options.maxBytesPerFile = 10240
	mustStartNSQd(options)

	topicName := "nsqd_test" + strconv.Itoa(int(time.Now().Unix()))

	exitChan := make(chan int)
	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
	topic := nsqd.GetTopic(topicName)
	for i := 0; i < iterations; i++ {
		msg := nsq.NewMessage(<-nsqd.idChan, body)
		topic.PutMessage(msg)
	}

	log.Printf("pulling from channel")
	channel1 := topic.GetChannel("ch1")

	log.Printf("read %d msgs", iterations/2)
	for i := 0; i < iterations/2; i++ {
		msg := <-channel1.clientMsgChan
		log.Printf("read message %d", i+1)
		assert.Equal(t, msg.Body, body)
	}

	for {
		if channel1.Depth() == int64(iterations/2) {
			break
		}
		time.Sleep(50 * time.Millisecond)
	}

	exitChan <- 1
	<-doneExitChan

	// start up a new nsqd w/ the same folder

	options = NewNsqdOptions()
	options.memQueueSize = 100
	options.maxBytesPerFile = 10240
	mustStartNSQd(options)

	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	topic = nsqd.GetTopic(topicName)
	// should be empty; channel should have drained everything
	count := topic.Depth()
	assert.Equal(t, count, int64(0))

	channel1 = topic.GetChannel("ch1")

	chan_count := channel1.Depth()
	assert.Equal(t, chan_count, int64(iterations/2))

	// read the other half of the messages
	for i := 0; i < iterations/2; i++ {
		msg := <-channel1.clientMsgChan
		log.Printf("read message %d", i+1)
		assert.Equal(t, msg.Body, body)
	}

	// verify we drained things
	assert.Equal(t, len(topic.memoryMsgChan), 0)
	assert.Equal(t, topic.backend.Depth(), int64(0))

	exitChan <- 1
	<-doneExitChan
}

func TestEphemeralChannel(t *testing.T) {
	// a normal channel sticks around after clients disconnect; an ephemeral channel is
	// lazily removed after the last client disconnects
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	options := NewNsqdOptions()
	options.memQueueSize = 100
	mustStartNSQd(options)

	topicName := "ephemeral_test" + strconv.Itoa(int(time.Now().Unix()))
	doneExitChan := make(chan int)

	exitChan := make(chan int)
	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	body := []byte("an_ephemeral_message")
	topic := nsqd.GetTopic(topicName)
	ephemeralChannel := topic.GetChannel("ch1#ephemeral")

	msg := nsq.NewMessage(<-nsqd.idChan, body)
	topic.PutMessage(msg)
	msg = <-ephemeralChannel.clientMsgChan
	assert.Equal(t, msg.Body, body)

	log.Printf("pulling from channel")
	ephemeralChannel.RemoveClient(nil)

	time.Sleep(50 * time.Millisecond)

	assert.Equal(t, len(topic.channelMap), 0)
	exitChan <- 1
	<-doneExitChan
}
<commit_msg>simplify test message creation<commit_after>package main

import (
	"github.com/bitly/nsq/nsq"
	"github.com/bmizerany/assert"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestStartup(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	iterations := 300
	doneExitChan := make(chan int)

	options := NewNsqdOptions()
	options.memQueueSize = 100
	options.maxBytesPerFile = 10240
	mustStartNSQd(options)

	topicName := "nsqd_test" + strconv.Itoa(int(time.Now().Unix()))

	exitChan := make(chan int)
	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	body := make([]byte, 256)
	topic := nsqd.GetTopic(topicName)
	for i := 0; i < iterations; i++ {
		msg := nsq.NewMessage(<-nsqd.idChan, body)
		topic.PutMessage(msg)
	}

	log.Printf("pulling from channel")
	channel1 := topic.GetChannel("ch1")

	log.Printf("read %d msgs", iterations/2)
	for i := 0; i < iterations/2; i++ {
		msg := <-channel1.clientMsgChan
		log.Printf("read message %d", i+1)
		assert.Equal(t, msg.Body, body)
	}

	for {
		if channel1.Depth() == int64(iterations/2) {
			break
		}
		time.Sleep(50 * time.Millisecond)
	}

	exitChan <- 1
	<-doneExitChan

	// start up a new nsqd w/ the same folder

	options = NewNsqdOptions()
	options.memQueueSize = 100
	options.maxBytesPerFile = 10240
	mustStartNSQd(options)

	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	topic = nsqd.GetTopic(topicName)
	// should be empty; channel should have drained everything
	count := topic.Depth()
	assert.Equal(t, count, int64(0))

	channel1 = topic.GetChannel("ch1")

	chan_count := channel1.Depth()
	assert.Equal(t, chan_count, int64(iterations/2))

	// read the other half of the messages
	for i := 0; i < iterations/2; i++ {
		msg := <-channel1.clientMsgChan
		log.Printf("read message %d", i+1)
		assert.Equal(t, msg.Body, body)
	}

	// verify we drained things
	assert.Equal(t, len(topic.memoryMsgChan), 0)
	assert.Equal(t, topic.backend.Depth(), int64(0))

	exitChan <- 1
	<-doneExitChan
}

func TestEphemeralChannel(t *testing.T) {
	// a normal channel sticks around after clients disconnect; an ephemeral channel is
	// lazily removed after the last client disconnects
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	options := NewNsqdOptions()
	options.memQueueSize = 100
	mustStartNSQd(options)

	topicName := "ephemeral_test" + strconv.Itoa(int(time.Now().Unix()))
	doneExitChan := make(chan int)

	exitChan := make(chan int)
	go func() {
		<-exitChan
		nsqd.Exit()
		doneExitChan <- 1
	}()

	body := []byte("an_ephemeral_message")
	topic := nsqd.GetTopic(topicName)
	ephemeralChannel := topic.GetChannel("ch1#ephemeral")

	msg := nsq.NewMessage(<-nsqd.idChan, body)
	topic.PutMessage(msg)
	msg = <-ephemeralChannel.clientMsgChan
	assert.Equal(t, msg.Body, body)

	log.Printf("pulling from channel")
	ephemeralChannel.RemoveClient(nil)

	time.Sleep(50 * time.Millisecond)

	assert.Equal(t, len(topic.channelMap), 0)
	exitChan <- 1
	<-doneExitChan
}
<|endoftext|>


<commit_before>// Copyright 2013 <me@cwchang.me>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package opencv

//#include "opencv.h"
//#cgo linux  pkg-config: opencv
//#cgo darwin pkg-config: opencv
//#cgo freebsd pkg-config: opencv
//#cgo windows LDFLAGS: -lopencv_core242.dll -lopencv_imgproc242.dll -lopencv_photo242.dll -lopencv_highgui242.dll -lstdc++
import "C"
import (
	//"errors"
	//"log"
	"unsafe"
)

const (
	CV_INTER_NN       = int(C.CV_INTER_NN)
	CV_INTER_LINEAR   = int(C.CV_INTER_LINEAR)
	CV_INTER_CUBIC    = int(C.CV_INTER_CUBIC)
	CV_INTER_AREA     = int(C.CV_INTER_AREA)
	CV_INTER_LANCZOS4 = int(C.CV_INTER_LANCZOS4)
)

func Resize(src *IplImage, width, height, interpolation int) *IplImage {
	if width == 0 && height == 0 {
		panic("Width and Height cannot be 0 at the same time")
	}
	if width == 0 {
		ratio := float64(height) / float64(src.Height())
		width = int(float64(src.Width()) * ratio)
	} else if height == 0 {
		ratio := float64(width) / float64(src.Width())
		height = int(float64(src.Height()) * ratio)
	}

	dst := CreateImage(width, height, src.Depth(), src.Channels())
	C.cvResize(unsafe.Pointer(src), unsafe.Pointer(dst), C.int(interpolation))
	return dst
}

func Crop(src *IplImage, x, y, width, height int) *IplImage {
	r := C.cvRect(C.int(x), C.int(y), C.int(width), C.int(height))
	rect := Rect(r)

	src.SetROI(rect)
	dest := CreateImage(width, height, src.Depth(), src.Channels())
	Copy(src, dest, nil)
	src.ResetROI()

	return dest
}

/* Returns a Seq of countours in an image, detected according to the parameters.
   Caller must Release() the Seq returned */
func (image *IplImage) FindContours(mode, method int, offset Point) *Seq {
	storage := C.cvCreateMemStorage(0)
	header_size := (C.size_t)(unsafe.Sizeof(C.CvContour{}))
	var seq *C.CvSeq
	C.cvFindContours(
		unsafe.Pointer(image),
		storage,
		&seq,
		C.int(header_size),
		C.int(mode),
		C.int(method),
		C.cvPoint(C.int(offset.X), C.int(offset.Y)))

	return (*Seq)(seq)
}

//cvDrawContours(CvArr* img, CvSeq* contour, CvScalar externalColor, CvScalar holeColor, int maxLevel, int thickness=1, int lineType=8
func DrawContours(image *IplImage, contours *Seq, externalColor, holeColor Scalar, maxLevel, thickness, lineType int, offset Point) {
	C.cvDrawContours(
		unsafe.Pointer(image),
		(*C.CvSeq)(contours),
		(C.CvScalar)(externalColor),
		(C.CvScalar)(holeColor),
		C.int(maxLevel),
		C.int(thickness),
		C.int(lineType),
		C.cvPoint(C.int(offset.X), C.int(offset.Y)))
}

// CvSeq* cvApproxPoly(const void* src_seq, int header_size, CvMemStorage* storage, int method, double eps, int recursive=0 )
func ApproxPoly(src *Seq, header_size int, storage *MemStorage, method int, eps float64, recursive int) *Seq {
	seq := C.cvApproxPoly(
		unsafe.Pointer(src),
		C.int(header_size),
		(*C.CvMemStorage)(storage),
		C.int(method),
		C.double(eps),
		C.int(recursive))
	return (*Seq)(seq)
}

// cvArcLength(const void* curve, CvSlice slice=CV_WHOLE_SEQ, int is_closed=-1 )
func ArcLength(curve *Seq, slice Slice, is_closed bool) float64 {
	is_closed_int := 0
	if is_closed {
		is_closed_int = 1
	}
	return float64(C.cvArcLength(unsafe.Pointer(curve),
		(C.CvSlice)(slice),
		C.int(is_closed_int)))
}

func ContourPerimeter(curve *Seq) float64 {
	return ArcLength(curve, WholeSeq(), true)
}

// double cvContourArea(const CvArr* contour, CvSlice slice=CV_WHOLE_SEQ, int oriented=0 )
func ContourArea(contour *Seq, slice Slice, oriented int) float64 {
	return float64(C.cvContourArea(
		unsafe.Pointer(contour),
		(C.CvSlice)(slice),
		C.int(oriented)))
}

/* points can be either CvSeq* or CvMat* */
func FitEllipse2(points unsafe.Pointer) Box2D {
	box := C.cvFitEllipse2(points)
	center := Point2D32f{float32(box.center.x), float32(box.center.y)}
	size := Size2D32f{float32(box.size.width), float32(box.size.height)}
	angle := float32(box.angle)
	return Box2D{center, size, angle}
}
<commit_msg>added MinAreaRect()<commit_after>// Copyright 2013 <me@cwchang.me>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package opencv

//#include "opencv.h"
//#cgo linux  pkg-config: opencv
//#cgo darwin pkg-config: opencv
//#cgo freebsd pkg-config: opencv
//#cgo windows LDFLAGS: -lopencv_core242.dll -lopencv_imgproc242.dll -lopencv_photo242.dll -lopencv_highgui242.dll -lstdc++
import "C"
import (
	//"errors"
	//"log"
	"unsafe"
)

const (
	CV_INTER_NN       = int(C.CV_INTER_NN)
	CV_INTER_LINEAR   = int(C.CV_INTER_LINEAR)
	CV_INTER_CUBIC    = int(C.CV_INTER_CUBIC)
	CV_INTER_AREA     = int(C.CV_INTER_AREA)
	CV_INTER_LANCZOS4 = int(C.CV_INTER_LANCZOS4)
)

func Resize(src *IplImage, width, height, interpolation int) *IplImage {
	if width == 0 && height == 0 {
		panic("Width and Height cannot be 0 at the same time")
	}
	if width == 0 {
		ratio := float64(height) / float64(src.Height())
		width = int(float64(src.Width()) * ratio)
	} else if height == 0 {
		ratio := float64(width) / float64(src.Width())
		height = int(float64(src.Height()) * ratio)
	}

	dst := CreateImage(width, height, src.Depth(), src.Channels())
	C.cvResize(unsafe.Pointer(src), unsafe.Pointer(dst), C.int(interpolation))
	return dst
}

func Crop(src *IplImage, x, y, width, height int) *IplImage {
	r := C.cvRect(C.int(x), C.int(y), C.int(width), C.int(height))
	rect := Rect(r)

	src.SetROI(rect)
	dest := CreateImage(width, height, src.Depth(), src.Channels())
	Copy(src, dest, nil)
	src.ResetROI()

	return dest
}

/* Returns a Seq of countours in an image, detected according to the parameters.
   Caller must Release() the Seq returned */
func (image *IplImage) FindContours(mode, method int, offset Point) *Seq {
	storage := C.cvCreateMemStorage(0)
	header_size := (C.size_t)(unsafe.Sizeof(C.CvContour{}))
	var seq *C.CvSeq
	C.cvFindContours(
		unsafe.Pointer(image),
		storage,
		&seq,
		C.int(header_size),
		C.int(mode),
		C.int(method),
		C.cvPoint(C.int(offset.X), C.int(offset.Y)))

	return (*Seq)(seq)
}

//cvDrawContours(CvArr* img, CvSeq* contour, CvScalar externalColor, CvScalar holeColor, int maxLevel, int thickness=1, int lineType=8
func DrawContours(image *IplImage, contours *Seq, externalColor, holeColor Scalar, maxLevel, thickness, lineType int, offset Point) {
	C.cvDrawContours(
		unsafe.Pointer(image),
		(*C.CvSeq)(contours),
		(C.CvScalar)(externalColor),
		(C.CvScalar)(holeColor),
		C.int(maxLevel),
		C.int(thickness),
		C.int(lineType),
		C.cvPoint(C.int(offset.X), C.int(offset.Y)))
}

// CvSeq* cvApproxPoly(const void* src_seq, int header_size, CvMemStorage* storage, int method, double eps, int recursive=0 )
func ApproxPoly(src *Seq, header_size int, storage *MemStorage, method int, eps float64, recursive int) *Seq {
	seq := C.cvApproxPoly(
		unsafe.Pointer(src),
		C.int(header_size),
		(*C.CvMemStorage)(storage),
		C.int(method),
		C.double(eps),
		C.int(recursive))
	return (*Seq)(seq)
}

// cvArcLength(const void* curve, CvSlice slice=CV_WHOLE_SEQ, int is_closed=-1 )
func ArcLength(curve *Seq, slice Slice, is_closed bool) float64 {
	is_closed_int := 0
	if is_closed {
		is_closed_int = 1
	}
	return float64(C.cvArcLength(unsafe.Pointer(curve),
		(C.CvSlice)(slice),
		C.int(is_closed_int)))
}

func ContourPerimeter(curve *Seq) float64 {
	return ArcLength(curve, WholeSeq(), true)
}

// double cvContourArea(const CvArr* contour, CvSlice slice=CV_WHOLE_SEQ, int oriented=0 )
func ContourArea(contour *Seq, slice Slice, oriented int) float64 {
	return float64(C.cvContourArea(
		unsafe.Pointer(contour),
		(C.CvSlice)(slice),
		C.int(oriented)))
}

/* points can be either CvSeq* or CvMat* */
func FitEllipse2(points unsafe.Pointer) Box2D {
	box := C.cvFitEllipse2(points)
	center := Point2D32f{float32(box.center.x), float32(box.center.y)}
	size := Size2D32f{float32(box.size.width), float32(box.size.height)}
	angle := float32(box.angle)
	return Box2D{center, size, angle}
}

// Finds a rotated rectangle of the minimum area enclosing the input 2D point set
// points can be either CvSeq* or CvMat*
func MinAreaRect(points unsafe.Pointer) Box2D {
	box := C.cvMinAreaRect2(points, nil)
	center := Point2D32f{float32(box.center.x), float32(box.center.y)}
	size := Size2D32f{float32(box.size.width), float32(box.size.height)}
	angle := float32(box.angle)
	return Box2D{center, size, angle}
}
<|endoftext|>


<commit_before>//
// Copyright © 2016 Intel Corporation
//
// 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 osprepare

import (
	"fmt"
	"os"
	"strings"
)

// Minimal versions supported by ciao
const (
	MinDockerVersion = "1.11.0"
	MinQemuVersion   = "2.5.0"
)

// PackageRequirement contains the BinaryName expected to
// exist on the filesystem once PackageName is installed
// (e.g: { '/usr/bin/qemu-system-x86_64', 'qemu'})
type PackageRequirement struct {
	BinaryName  string
	PackageName string
}

// PackageRequirements type allows to create complex
// mapping to group a set of PackageRequirement to a single
// key.
// (e.g:
//
//	"ubuntu": {
//		{"/usr/bin/docker", "docker"},
//	},
//	"clearlinux": {
//		{"/usr/bin/docker", "containers-basic"},
//	},
// )
type PackageRequirements map[string][]PackageRequirement

// BootstrapRequirements lists required dependencies for absolutely core
// functionality across all Ciao components
var BootstrapRequirements = PackageRequirements{
	"ubuntu": {
		{"/usr/bin/cephfs", "ceph-fs-common"},
		{"/usr/bin/ceph", "ceph-common"},
	},
	"clearlinux": {
		{"/usr/bin/ceph", "storage-cluster"},
	},
}

// CollectPackages returns a list of non-installed packages from
// the PackageRequirements received
func collectPackages(dist distro, reqs PackageRequirements) []string {
	// For now just support keys like "ubuntu" vs "ubuntu:16.04"
	var pkgsMissing []string
	if reqs == nil {
		return nil
	}

	id := dist.getID()
	if pkgs, success := reqs[id]; success {
		for _, pkg := range pkgs {
			// Have the path existing, skip.
			if pathExists(pkg.BinaryName) {
				continue
			}
			// Mark the package for installation
			pkgsMissing = append(pkgsMissing, pkg.PackageName)
		}
		return pkgsMissing
	}
	return nil
}

// InstallDeps installs all the dependencies defined in a component
// specific PackageRequirements in order to enable running the component
func InstallDeps(reqs PackageRequirements) {
	distro := getDistro()

	if distro == nil {
		fmt.Fprintf(os.Stderr, "Running on an unsupported distro\n")
		if rel := getOSRelease(); rel != nil {
			fmt.Fprintf(os.Stderr, "Unsupported distro: %s %s\n", rel.Name, rel.Version)
		} else {
			fmt.Fprintln(os.Stderr, "No os-release found on this host")
		}
		return
	}
	// Nothing requested to install
	if reqs == nil {
		return
	}
	if reqPkgs := collectPackages(distro, reqs); reqPkgs != nil {
		if distro.InstallPackages(reqPkgs) == false {
			fmt.Fprintf(os.Stderr, "Failed to install: %s\n", strings.Join(reqPkgs, ", "))
		}
	}
}

// Bootstrap installs all the core dependencies required to bootstrap the core
// configuration of all Ciao components
func Bootstrap() {
	InstallDeps(BootstrapRequirements)
}
<commit_msg>osprepare: don't add empty dependencies to the required list<commit_after>//
// Copyright © 2016 Intel Corporation
//
// 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 osprepare

import (
	"fmt"
	"os"
	"strings"
)

// Minimal versions supported by ciao
const (
	MinDockerVersion = "1.11.0"
	MinQemuVersion   = "2.5.0"
)

// PackageRequirement contains the BinaryName expected to
// exist on the filesystem once PackageName is installed
// (e.g: { '/usr/bin/qemu-system-x86_64', 'qemu'})
type PackageRequirement struct {
	BinaryName  string
	PackageName string
}

// PackageRequirements type allows to create complex
// mapping to group a set of PackageRequirement to a single
// key.
// (e.g:
//
//	"ubuntu": {
//		{"/usr/bin/docker", "docker"},
//	},
//	"clearlinux": {
//		{"/usr/bin/docker", "containers-basic"},
//	},
// )
type PackageRequirements map[string][]PackageRequirement

// BootstrapRequirements lists required dependencies for absolutely core
// functionality across all Ciao components
var BootstrapRequirements = PackageRequirements{
	"ubuntu": {
		{"/usr/bin/cephfs", "ceph-fs-common"},
		{"/usr/bin/ceph", "ceph-common"},
	},
	"clearlinux": {
		{"/usr/bin/ceph", "storage-cluster"},
	},
}

// CollectPackages returns a list of non-installed packages from
// the PackageRequirements received
func collectPackages(dist distro, reqs PackageRequirements) []string {
	// For now just support keys like "ubuntu" vs "ubuntu:16.04"
	var pkgsMissing []string
	if reqs == nil {
		return nil
	}

	id := dist.getID()
	if pkgs, success := reqs[id]; success {
		for _, pkg := range pkgs {
			// skip empties
			if pkg.BinaryName == "" || pkg.PackageName == "" {
				continue
			}

			// Have the path existing, skip.
			if pathExists(pkg.BinaryName) {
				continue
			}
			// Mark the package for installation
			pkgsMissing = append(pkgsMissing, pkg.PackageName)
		}
		return pkgsMissing
	}
	return nil
}

// InstallDeps installs all the dependencies defined in a component
// specific PackageRequirements in order to enable running the component
func InstallDeps(reqs PackageRequirements) {
	distro := getDistro()

	if distro == nil {
		fmt.Fprintf(os.Stderr, "Running on an unsupported distro\n")
		if rel := getOSRelease(); rel != nil {
			fmt.Fprintf(os.Stderr, "Unsupported distro: %s %s\n", rel.Name, rel.Version)
		} else {
			fmt.Fprintln(os.Stderr, "No os-release found on this host")
		}
		return
	}
	// Nothing requested to install
	if reqs == nil {
		return
	}
	if reqPkgs := collectPackages(distro, reqs); reqPkgs != nil {
		if distro.InstallPackages(reqPkgs) == false {
			fmt.Fprintf(os.Stderr, "Failed to install: %s\n", strings.Join(reqPkgs, ", "))
		}
	}
}

// Bootstrap installs all the core dependencies required to bootstrap the core
// configuration of all Ciao components
func Bootstrap() {
	InstallDeps(BootstrapRequirements)
}
<|endoftext|>


<commit_before>// echo -- print arguments to standard output
// Part of goutils (https://github.com/trevorparker/goutils)
//
// Copyright (c) 2013 Trevor Parker <trevor@trevorparker.com>
// All rights reserved
//
// Distributed under the terms of the modified BSD license (see LICENSE)

package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"unicode/utf8"
)

const usage_message string = "usage: echo [OPTION ...] [STRING ...]"
const help_message string = `Print STRING arguments to STDOUT.
Backslash escape sequences in STRING are interpreted.

  -n,                       do not print a trailing newline character
  -h, --help                print this help message and exit
`

func usage(error string) {
	fmt.Fprintf(os.Stderr, "echo: %s\n%s\n", error, usage_message)
	os.Exit(1)
}

func help() {
	fmt.Printf("%s\n%s", usage_message, help_message)
	os.Exit(0)
}

func main() {
	start := 1
	trailing := "\n"
	if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
		help()
	}
	if len(os.Args) > 1 && os.Args[1] == "-n" {
		start = 2
		trailing = ""
	}
	var b bytes.Buffer
	skip_next := false
	arg_string := strings.Join(os.Args[start:], " ")
	for i, c := range arg_string {
		if skip_next == true {
			skip_next = false
			continue
		} else {
			skip_next = true
		}
		r, _ := utf8.DecodeRune([]byte("\\"))
		if c == r && i < len(arg_string) {
			switch d, _ := utf8.DecodeRune([]byte(arg_string[i+1 : i+2])); d {
			case 'a':
				c, _ = utf8.DecodeRune([]byte("\a"))
			case 'b':
				c, _ = utf8.DecodeRune([]byte("\b"))
			case 'c':
				print(b.String() + trailing)
				os.Exit(0)
			case 'e':
				c, _ = utf8.DecodeRune([]byte("\x1B"))
			case 'f':
				c, _ = utf8.DecodeRune([]byte("\f"))
			case 'n':
				c, _ = utf8.DecodeRune([]byte("\n"))
			case 'r':
				c, _ = utf8.DecodeRune([]byte("\r"))
			case 't':
				c, _ = utf8.DecodeRune([]byte("\t"))
			case 'v':
				c, _ = utf8.DecodeRune([]byte("\v"))
			default:
				skip_next = false
			}
		} else {
			skip_next = false
		}
		b.WriteRune(c)
	}
	print(b.String() + trailing)
}
<commit_msg>Correctly handle `\c` to suppress newline<commit_after>// echo -- print arguments to standard output
// Part of goutils (https://github.com/trevorparker/goutils)
//
// Copyright (c) 2013 Trevor Parker <trevor@trevorparker.com>
// All rights reserved
//
// Distributed under the terms of the modified BSD license (see LICENSE)

package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"unicode/utf8"
)

const usage_message string = "usage: echo [OPTION ...] [STRING ...]"
const help_message string = `Print STRING arguments to STDOUT.
Backslash escape sequences in STRING are interpreted.

  -n,                       do not print a trailing newline character
  -h, --help                print this help message and exit
`

func usage(error string) {
	fmt.Fprintf(os.Stderr, "echo: %s\n%s\n", error, usage_message)
	os.Exit(1)
}

func help() {
	fmt.Printf("%s\n%s", usage_message, help_message)
	os.Exit(0)
}

func main() {
	start := 1
	trailing := "\n"
	if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
		help()
	}
	if len(os.Args) > 1 && os.Args[1] == "-n" {
		start = 2
		trailing = ""
	}
	var b bytes.Buffer
	skip_next := false
	arg_string := strings.Join(os.Args[start:], " ")
	for i, c := range arg_string {
		if skip_next == true {
			skip_next = false
			continue
		} else {
			skip_next = true
		}
		r, _ := utf8.DecodeRune([]byte("\\"))
		if c == r && i < len(arg_string) {
			switch d, _ := utf8.DecodeRune([]byte(arg_string[i+1 : i+2])); d {
			case 'a':
				c, _ = utf8.DecodeRune([]byte("\a"))
			case 'b':
				c, _ = utf8.DecodeRune([]byte("\b"))
			case 'c':
				print(b.String())
				os.Exit(0)
			case 'e':
				c, _ = utf8.DecodeRune([]byte("\x1B"))
			case 'f':
				c, _ = utf8.DecodeRune([]byte("\f"))
			case 'n':
				c, _ = utf8.DecodeRune([]byte("\n"))
			case 'r':
				c, _ = utf8.DecodeRune([]byte("\r"))
			case 't':
				c, _ = utf8.DecodeRune([]byte("\t"))
			case 'v':
				c, _ = utf8.DecodeRune([]byte("\v"))
			default:
				skip_next = false
			}
		} else {
			skip_next = false
		}
		b.WriteRune(c)
	}
	print(b.String() + trailing)
}
<|endoftext|>


<commit_before>package emit

import (
	"bufio"
	"fmt"
	"github.com/andrewchambers/g/parse"
	"strings"
)

type emitter struct {
	gscope   *scope
	curscope *scope
	out      *bufio.Writer

    llvmNameCounter uint
	curFuncType *GFunc
}

type Value interface {
	getLLVMRepr() string
	isLVal() bool
	getGType() GType
}

type exprValue struct {
	llvmName string
	lval     bool
	gType    GType
}

type intConstant struct {
	val int64
}

type boolConstant struct {
	val bool
}

func (v *exprValue) getLLVMRepr() string {
	return v.llvmName
}

func (v *exprValue) isLVal() bool {
	return v.lval
}

func (v *exprValue) getGType() GType {
	return v.gType
}

func (c *intConstant) getLLVMRepr() string {
	return fmt.Sprintf("%v", c.val)
}

func (c *intConstant) isLVal() bool {
	return false
}

func (c *intConstant) getGType() GType {
	return &GConstant{}
}

func (c *boolConstant) getLLVMRepr() string {
	if c.val {
	    return "1"
	}
	return "0"
}

func (c *boolConstant) isLVal() bool {
	return false
}

func (c *boolConstant) getGType() GType {
	return NewGInt(1,false)
}

func newEmitter(out *bufio.Writer) *emitter {
	ret := &emitter{}
	ret.curscope = newScope(nil)
	ret.gscope = ret.curscope
	ret.out = out
	ret.addBuiltinTypes()
	return ret
}

func (e *emitter) newLLVMName() string {
    e.llvmNameCounter++
	return fmt.Sprintf("%%%d",e.llvmNameCounter)
}

func (e *emitter) addBuiltinTypes() {
	e.curscope.declareType("bool",NewGInt(1, false))
	e.curscope.declareType("int", NewGInt(e.getIntWidth(), true))
	e.curscope.declareType("uint",NewGInt(e.getIntWidth(), false))
}

//XXX shift to arch type
func (e *emitter) getIntWidth() uint {
	return 64
}

func (e *emitter) pushScope() {
	e.curscope = newScope(e.curscope)
}

func (e *emitter) popScope() {
	e.curscope = e.curscope.parent
}

func (e *emitter) emit(s string, args ...interface{}) {
	fmt.Fprintf(e.out, s, args...)
}

func (e *emitter) emiti(s string, args ...interface{}) {
	e.emit("    "+s, args...)
}

func EmitModule(out *bufio.Writer, file *parse.File) error {
	e := newEmitter(out)
	err := e.collectGlobalSymbols(file)
	if err != nil {
		return err
	}
	e.emit("; compiled from file %s\n", file.Span.Path)
	e.emit("target triple = \"x86_64-pc-linux-gnu\"\n\n")
	for _, fd := range file.FuncDecls {
		err = e.emitFuncDecl(fd)
		if err != nil {
			return err
		}
	}
	out.Flush()
	return nil
}

func (e *emitter) collectGlobalSymbols(file *parse.File) error {
	for _, imp := range file.Imports {
		impdef := imp.Val[1 : len(imp.Val)-1]
		impdef = strings.Split(impdef, "/")[0]
		sym := newGlobalSymbol(imp.Span.Start)
		err := e.curscope.declareSym(impdef, sym)
		if err != nil {
			return fmt.Errorf("bad import: %s %s:%v", err, imp.Span.Path, imp.Span.Start)
		}
	}
	for _, fd := range file.FuncDecls {
		sym := newGlobalSymbol(fd.Span.Start)
		err := e.curscope.declareSym(fd.Name, sym)
		if err != nil {
			return fmt.Errorf("bad func decl: %s %s:%v", err, fd.Span.Path, fd.Span.Start)
		}
	}
	for _, vd := range file.VarDecls {
		sym := newGlobalSymbol(vd.Span.Start)
		err := e.curscope.declareSym(vd.Name, sym)
		if err != nil {
			return fmt.Errorf("bad var decl: %s %s:%v", err, vd.Span.Path, vd.Span.Start)
		}
	}
	return nil
}

func (e *emitter) emitFuncDecl(f *parse.FuncDecl) error {
	//Emit function start
	ft, err := e.funcDeclToGType(f)
	if err != nil {
		return err
	}
	e.curFuncType = ft
	e.emit("define %s @%s() {\n", gTypeToLLVM(ft.RetType), f.Name)
	for _, stmt := range f.Body {
		e.emitStatement(stmt)
	}
	e.emit("}\n")
	return nil
}

func (e *emitter) emitStatement(stmt parse.Node) {
	switch stmt := stmt.(type) {
	case *parse.VarDecl:
	    e.emitLocalVarDecl(stmt)
	case *parse.Return:
		e.emitReturn(stmt)
	default:
		panic("unhandled Statement type...")
	}
}

func (e *emitter) emitLocalVarDecl(vd *parse.VarDecl) {
    t,err := e.parseNodeToGType(vd.Type)
    if err != nil {
        panic("unhandled err")
    }
    name := e.newLLVMName()
    e.emiti("%s = alloca %s\n",name,gTypeToLLVM(t))
    e.emitZeroMem(name,t)
    s := &localSymbol{
         alloca: name,
         gType:  t,
         defPos: vd.Span.Start,
    }
    e.curscope.declareSym(vd.Name,s)
}

func (e *emitter) emitReturn(r *parse.Return) {
	var err error
	v := e.emitExpression(r.Expr)
	if v.isLVal() {
	    v,err = e.emitRemoveLValness(v)
	    if err != nil {
	        panic("error emitting return " + err.Error())
	    }
	}
	var llvmType string
	_, ok := v.getGType().(*GConstant)
	if ok {
		_, ok := e.curFuncType.RetType.(*GInt)
		if !ok {
			// XXX
			panic("cannot cast constant to return type")
		}
		llvmType = gTypeToLLVM(e.curFuncType.RetType)
	} else {
		if !v.getGType().Equals(e.curFuncType.RetType) {
			panic("failed type check")
		}
		llvmType = gTypeToLLVM(v.getGType())

	}
	e.emiti("ret %s %s\n", llvmType, v.getLLVMRepr())
}

func (e *emitter) emitZeroMem(name string,t GType) error {
    switch t := t.(type) {
        case *GInt:
            switch t.Bits {
                case 64:
                    e.emiti("store i64 0, i64* %s\n",name)
                case 32:
                    e.emiti("store i32 0, i32* %s\n",name)
                case 16:
                    e.emiti("store i16 0, i16* %s\n",name)
                case 8:
                    e.emiti("store i8 0, i8* %s\n",name)
                case 1:
                    e.emiti("store i1 0, i1* %s\n",name)
                default:
                    panic("internal error")
            }
            return nil
        default:
            return fmt.Errorf("unable to zero memory for type %s",t)
    }
}

func (e *emitter) emitRemoveLValness(v Value) (Value,error) {
    if !v.isLVal() {
        panic("internal error")
    }
    switch v := v.(type) {
        case *exprValue:
            name := e.newLLVMName()
            e.emiti("%s = load %s* %s\n",name,gTypeToLLVM(v.getGType()),v.getLLVMRepr())
            ret := &exprValue{
                llvmName: name,
	            lval:     false,
	            gType:    v.getGType(),
            }
            return ret,nil
        default:
            panic("internal error")
    }
}

func (e *emitter) emitExpression(expr parse.Node) Value {
	switch expr := expr.(type) {
	case *parse.Constant:
		v := &intConstant{expr.Val}
		return v
	case *parse.Binop:
		return e.emitBinop(expr)
	case *parse.Ident:
	    return e.emitIdent(expr)
	default:
		panic("unhandled...")
	}
}

func (e *emitter) emitIdent(i *parse.Ident) Value {
    s,err := e.curscope.lookupSym(i.Val)
    if err != nil {
        panic(err)
    }
    switch s := s.(type) {
        case *localSymbol:
            return &exprValue{
                llvmName: s.alloca,
                lval: true,
                gType: s.getGType(),
            }
        default:
            panic("unhandled...")
    }
}

func isConstantVal(v Value) bool {
	_, ok := v.(*intConstant)
	if ok {
	    return true
	}
	_, ok = v.(*boolConstant)
	if ok {
	    return true
	}
	return false
}

func isIntType(t GType) bool {
	_, ok := t.(*GInt)
	return ok
}

func (e *emitter) emitBinop(b *parse.Binop) Value {

	l := e.emitExpression(b.L)
	r := e.emitExpression(b.R)

	if isConstantVal(l) && isConstantVal(r) {
		c, err := foldConstantBinop(b.Op, l, r)
		if err != nil {
			panic(err)
		}
		return c
	}

	lstr := fmt.Sprintf("%s %s", gTypeToLLVM(l.getGType()), l.getLLVMRepr())
	rstr := fmt.Sprintf("%s %s", gTypeToLLVM(r.getGType()), r.getLLVMRepr())

	if !l.getGType().Equals(r.getGType()) {
		panic("arithmetic on incompatible types")
	}

	if !isIntType(l.getGType()) {
		panic("arithmetic on non int type")
	}

	switch b.Op {
	case '+':
		e.emiti("new = add %s %s\n", lstr, rstr)
	default:
		panic("unreachable")
	}

	panic("unreachable")
}

func (e *emitter) funcDeclToGType(f *parse.FuncDecl) (*GFunc, error) {
	ret := &GFunc{}
	for _, t := range f.ArgTypes {
		gty, err := e.parseNodeToGType(t)
		if err != nil {
			return nil, err
		}
		ret.ArgTypes = append(ret.ArgTypes, gty)
	}
	if f.RetType != nil {
		t, err := e.parseNodeToGType(f.RetType)
		if err != nil {
			return nil, err
		}
		ret.RetType = t
	}

	return ret, nil
}

func (e *emitter) parseNodeToGType(n parse.Node) (GType, error) {
	//span := n.GetSpan()
	switch n := n.(type) {
	case *parse.TypeAlias:
		ret, err := e.curscope.lookupType(n.Name)
		return ret,err
	default:
	    return nil,fmt.Errorf("invalid type")
	}

    panic("unreachable")
}

func gTypeToLLVM(t GType) string {
	switch t := t.(type) {
	case *GInt:
		switch t.Bits {
		case 64:
			return "i64"
		case 32:
			return "i32"
		case 16:
			return "i16"
		case 8:
			return "i8"
		case 1:
			return "i1"
		default:
			panic("unreachable.")
		}
	default:
		panic("unreachable")
	}
}
<commit_msg>work on emitting constants<commit_after>package emit

import (
	"bufio"
	"fmt"
	"github.com/andrewchambers/g/parse"
	"strings"
)

type emitter struct {
	gscope   *scope
	curscope *scope
	out      *bufio.Writer

    llvmNameCounter uint
	curFuncType *GFunc
}

type Value interface {
	getLLVMRepr() string
	isLVal() bool
	getGType() GType
}

type exprValue struct {
	llvmName string
	lval     bool
	gType    GType
}

type intConstant struct {
	val int64
}

type boolConstant struct {
	val bool
}

func (v *exprValue) getLLVMRepr() string {
	return v.llvmName
}

func (v *exprValue) isLVal() bool {
	return v.lval
}

func (v *exprValue) getGType() GType {
	return v.gType
}

func (c *intConstant) getLLVMRepr() string {
	return fmt.Sprintf("%v", c.val)
}

func (c *intConstant) isLVal() bool {
	return false
}

func (c *intConstant) getGType() GType {
	return &GConstant{}
}

func (c *boolConstant) getLLVMRepr() string {
	if c.val {
	    return "1"
	}
	return "0"
}

func (c *boolConstant) isLVal() bool {
	return false
}

func (c *boolConstant) getGType() GType {
	return NewGInt(1,false)
}

func newEmitter(out *bufio.Writer) *emitter {
	ret := &emitter{}
	ret.curscope = newScope(nil)
	ret.gscope = ret.curscope
	ret.out = out
	ret.addBuiltinTypes()
	return ret
}

func (e *emitter) newLLVMName() string {
    e.llvmNameCounter++
	return fmt.Sprintf("%%%d",e.llvmNameCounter)
}

func (e *emitter) addBuiltinTypes() {
	e.curscope.declareType("bool",NewGInt(1, false))
	e.curscope.declareType("int", NewGInt(e.getIntWidth(), true))
	e.curscope.declareType("uint",NewGInt(e.getIntWidth(), false))
	e.curscope.declareType("int32", NewGInt(32, true))
	e.curscope.declareType("uint32",NewGInt(32, false))
	e.curscope.declareType("int64", NewGInt(64, true))
	e.curscope.declareType("uint64",NewGInt(64, false))
}

//XXX shift to arch type
func (e *emitter) getIntWidth() uint {
	return 64
}

func (e *emitter) pushScope() {
	e.curscope = newScope(e.curscope)
}

func (e *emitter) popScope() {
	e.curscope = e.curscope.parent
}

func (e *emitter) emit(s string, args ...interface{}) {
	fmt.Fprintf(e.out, s, args...)
}

func (e *emitter) emiti(s string, args ...interface{}) {
	e.emit("    "+s, args...)
}

func EmitModule(out *bufio.Writer, file *parse.File) error {
	e := newEmitter(out)
	err := e.collectGlobalSymbols(file)
	if err != nil {
		return err
	}
	e.emit("; compiled from file %s\n", file.Span.Path)
	e.emit("target triple = \"x86_64-pc-linux-gnu\"\n\n")
	for _, fd := range file.FuncDecls {
		err = e.emitFuncDecl(fd)
		if err != nil {
			return err
		}
	}
	out.Flush()
	return nil
}

func (e *emitter) collectGlobalSymbols(file *parse.File) error {
	for _, imp := range file.Imports {
		impdef := imp.Val[1 : len(imp.Val)-1]
		impdef = strings.Split(impdef, "/")[0]
		sym := newGlobalSymbol(imp.Span.Start)
		err := e.curscope.declareSym(impdef, sym)
		if err != nil {
			return fmt.Errorf("bad import: %s %s:%v", err, imp.Span.Path, imp.Span.Start)
		}
	}
	for _, fd := range file.FuncDecls {
		sym := newGlobalSymbol(fd.Span.Start)
		err := e.curscope.declareSym(fd.Name, sym)
		if err != nil {
			return fmt.Errorf("bad func decl: %s %s:%v", err, fd.Span.Path, fd.Span.Start)
		}
	}
	for _, vd := range file.VarDecls {
		sym := newGlobalSymbol(vd.Span.Start)
		err := e.curscope.declareSym(vd.Name, sym)
		if err != nil {
			return fmt.Errorf("bad var decl: %s %s:%v", err, vd.Span.Path, vd.Span.Start)
		}
	}
	return nil
}

func (e *emitter) emitFuncDecl(f *parse.FuncDecl) error {
	//Emit function start
	ft, err := e.funcDeclToGType(f)
	if err != nil {
		return err
	}
	e.curFuncType = ft
	e.emit("define %s @%s() {\n", gTypeToLLVM(ft.RetType), f.Name)
	for _, stmt := range f.Body {
		e.emitStatement(stmt)
	}
	e.emit("}\n")
	return nil
}

func (e *emitter) emitStatement(stmt parse.Node) {
	switch stmt := stmt.(type) {
	case *parse.VarDecl:
	    e.emitLocalVarDecl(stmt)
	case *parse.Return:
		e.emitReturn(stmt)
	default:
		panic("unhandled Statement type...")
	}
}

func (e *emitter) emitLocalVarDecl(vd *parse.VarDecl) {
    t,err := e.parseNodeToGType(vd.Type)
    if err != nil {
        panic("unhandled err " + err.Error())
    }
    name := e.newLLVMName()
    e.emiti("%s = alloca %s\n",name,gTypeToLLVM(t))
    e.emitZeroMem(name,t)
    s := &localSymbol{
         alloca: name,
         gType:  t,
         defPos: vd.Span.Start,
    }
    e.curscope.declareSym(vd.Name,s)
}

func (e *emitter) emitReturn(r *parse.Return) {
	var err error
	v := e.emitExpression(r.Expr)
	if v.isLVal() {
	    v,err = e.emitRemoveLValness(v)
	    if err != nil {
	        panic("error emitting return " + err.Error())
	    }
	}
	var llvmType string
	if isConstantVal(v) {
		v,err = e.emitRemoveConstant(v,e.curFuncType.RetType)
		if err != nil {
		    panic("error converting constant to return type")
		}
		llvmType = gTypeToLLVM(e.curFuncType.RetType)
	} else {
		if !v.getGType().Equals(e.curFuncType.RetType) {
			panic("failed type check")
		}
		llvmType = gTypeToLLVM(v.getGType())

	}
	e.emiti("ret %s %s\n", llvmType, v.getLLVMRepr())
}

func (e *emitter) emitZeroMem(name string,t GType) error {
    switch t := t.(type) {
        case *GInt:
            switch t.Bits {
                case 64:
                    e.emiti("store i64 0, i64* %s\n",name)
                case 32:
                    e.emiti("store i32 0, i32* %s\n",name)
                case 16:
                    e.emiti("store i16 0, i16* %s\n",name)
                case 8:
                    e.emiti("store i8 0, i8* %s\n",name)
                case 1:
                    e.emiti("store i1 0, i1* %s\n",name)
                default:
                    panic("internal error")
            }
            return nil
        default:
            return fmt.Errorf("unable to zero memory for type %s",t)
    }
}

func (e *emitter) emitRemoveLValness(v Value) (Value,error) {
    if !v.isLVal() {
        panic("internal error")
    }
    switch v := v.(type) {
        case *exprValue:
            name := e.newLLVMName()
            e.emiti("%s = load %s* %s\n",name,gTypeToLLVM(v.getGType()),v.getLLVMRepr())
            ret := &exprValue{
                llvmName: name,
	            lval:     false,
	            gType:    v.getGType(),
            }
            return ret,nil
        default:
            panic("internal error")
    }
}

func (e *emitter) emitRemoveConstant(v Value,hint GType) (Value,error) {
    if !isConstantVal(v) {
        panic("internal error")
    }
    switch v := v.(type) {
        case *intConstant:
            switch hint := hint.(type) {
                case *GInt:
                    ret := &exprValue{
                        llvmName: fmt.Sprintf("%d",v.val),
	                    lval:     false,
	                    gType:    hint,
                    }
                    return ret,nil
                default:
                    return nil,fmt.Errorf("unable to convert constant to XXX")
            }
        default:
            return nil,fmt.Errorf("internal error emitRemoveConstantNess")
    }
}

func (e *emitter) emitExpression(expr parse.Node) Value {
	switch expr := expr.(type) {
	case *parse.Constant:
		v := &intConstant{expr.Val}
		return v
	case *parse.Binop:
		return e.emitBinop(expr)
	case *parse.Ident:
	    return e.emitIdent(expr)
	default:
		panic("unhandled...")
	}
}

func (e *emitter) emitIdent(i *parse.Ident) Value {
    s,err := e.curscope.lookupSym(i.Val)
    if err != nil {
        panic(err)
    }
    switch s := s.(type) {
        case *localSymbol:
            return &exprValue{
                llvmName: s.alloca,
                lval: true,
                gType: s.getGType(),
            }
        default:
            panic("unhandled...")
    }
}

func isConstantVal(v Value) bool {
	_, ok := v.(*intConstant)
	if ok {
	    return true
	}
	_, ok = v.(*boolConstant)
	if ok {
	    return true
	}
	return false
}

func isIntType(t GType) bool {
	_, ok := t.(*GInt)
	return ok
}

func (e *emitter) emitBinop(b *parse.Binop) Value {
    var err error
	l := e.emitExpression(b.L)
	r := e.emitExpression(b.R)

	if isConstantVal(l) && isConstantVal(r) {
		c, err := foldConstantBinop(b.Op, l, r)
		if err != nil {
			panic(err)
		}
		return c
	}

	if isConstantVal(l) {
	    l,err = e.emitRemoveConstant(l,r.getGType())
	    if err != nil {
	        panic(err.Error())
	    }
	}

	if isConstantVal(r) {
	    r,err = e.emitRemoveConstant(r,l.getGType())
	    if err != nil {
	        panic(err.Error())
	    }
	}

	if l.isLVal() {
	    l,err = e.emitRemoveLValness(l)
	    if err != nil {
	        panic(err.Error())
	    }
	}

	if r.isLVal() {
	    r,err = e.emitRemoveLValness(r)
	    if err != nil {
	        panic(err.Error())
	    }
	}

	lstr := fmt.Sprintf("%s %s", gTypeToLLVM(l.getGType()), l.getLLVMRepr())
	rstr := fmt.Sprintf("%s %s", gTypeToLLVM(r.getGType()), r.getLLVMRepr())

	if !l.getGType().Equals(r.getGType()) {
		panic("arithmetic on incompatible types")
	}

	if !isIntType(l.getGType()) {
		panic("arithmetic on non int type")
	}

    ret := &exprValue{
        llvmName: e.newLLVMName(),
        gType: l.getGType(),
        lval: false,
    }

	switch b.Op {
	case '+':
		e.emiti("%s = add %s %s\n",ret.llvmName, lstr, rstr)
		return ret
	default:
		panic("unreachable")
	}

	panic("unreachable")
}

func (e *emitter) funcDeclToGType(f *parse.FuncDecl) (*GFunc, error) {
	ret := &GFunc{}
	for _, t := range f.ArgTypes {
		gty, err := e.parseNodeToGType(t)
		if err != nil {
			return nil, err
		}
		ret.ArgTypes = append(ret.ArgTypes, gty)
	}
	if f.RetType != nil {
		t, err := e.parseNodeToGType(f.RetType)
		if err != nil {
			return nil, err
		}
		ret.RetType = t
	}

	return ret, nil
}

func (e *emitter) parseNodeToGType(n parse.Node) (GType, error) {
	//span := n.GetSpan()
	switch n := n.(type) {
	case *parse.TypeAlias:
		ret, err := e.curscope.lookupType(n.Name)
		return ret,err
	default:
	    return nil,fmt.Errorf("invalid type")
	}

    panic("unreachable")
}

func gTypeToLLVM(t GType) string {
	switch t := t.(type) {
	case *GInt:
		switch t.Bits {
		case 64:
			return "i64"
		case 32:
			return "i32"
		case 16:
			return "i16"
		case 8:
			return "i8"
		case 1:
			return "i1"
		default:
			panic("unreachable.")
		}
	default:
		panic("unreachable: bad gtype " + fmt.Sprintf("%s",t))
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package gfile provides easy-to-use operations for file system.
package gfile

import (
	"bytes"
	"errors"
	"github.com/gogf/gf/text/gstr"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/util/gconv"
)

const (
	// Separator for file system.
	Separator = string(filepath.Separator)
)

var (
	// Default perm for file opening.
	DefaultPerm = os.FileMode(0666)

	// The absolute file path for main package.
	// It can be only checked and set once.
	mainPkgPath = gtype.NewString()

	// selfPath is the current running binary path.
	// As it is most commonly used, it is so defined as an internal package variable.
	selfPath = ""

	// Temporary directory of system.
	tempDir = "/tmp"
)

func init() {
	// Initialize internal package variable: tempDir.
	if !Exists(tempDir) {
		tempDir = os.TempDir()
	}
	// Initialize internal package variable: selfPath.
	selfPath, _ := exec.LookPath(os.Args[0])
	if selfPath != "" {
		selfPath, _ = filepath.Abs(selfPath)
	}
	if selfPath == "" {
		selfPath, _ = filepath.Abs(os.Args[0])
	}
}

// Mkdir creates directories recursively with given <path>.
// The parameter <path> is suggested to be absolute path.
func Mkdir(path string) error {
	err := os.MkdirAll(path, os.ModePerm)
	if err != nil {
		return err
	}
	return nil
}

// Create creates file with given <path> recursively.
// The parameter <path> is suggested to be absolute path.
func Create(path string) (*os.File, error) {
	dir := Dir(path)
	if !Exists(dir) {
		if err := Mkdir(dir); err != nil {
			return nil, err
		}
	}
	return os.Create(path)
}

// Open opens file/directory READONLY.
func Open(path string) (*os.File, error) {
	return os.Open(path)
}

// OpenFile opens file/directory with custom <flag> and <perm>.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
	return os.OpenFile(path, flag, perm)
}

// OpenWithFlag opens file/directory with default perm and custom <flag>.
// The default <perm> is 0666.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
func OpenWithFlag(path string, flag int) (*os.File, error) {
	f, err := os.OpenFile(path, flag, DefaultPerm)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// OpenWithFlagPerm opens file/directory with custom <flag> and <perm>.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
// The parameter <perm> is like: 0600, 0666, 0777, etc.
func OpenWithFlagPerm(path string, flag int, perm os.FileMode) (*os.File, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// Join joins string array paths with file separator of current system.
func Join(paths ...string) string {
	var s string
	for _, path := range paths {
		if s != "" {
			s += Separator
		}
		s += gstr.TrimRight(path, Separator)
	}
	return s
}

// Exists checks whether given <path> exist.
func Exists(path string) bool {
	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
		return true
	}
	return false
}

// IsDir checks whether given <path> a directory.
func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return s.IsDir()
}

// Pwd returns absolute path of current working directory.
func Pwd() string {
	path, _ := os.Getwd()
	return path
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	return os.Chdir(dir)
}

// IsFile checks whether given <path> a file, which means it's not a directory.
func IsFile(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return !s.IsDir()
}

// Alias of Stat.
// See Stat.
func Info(path string) (os.FileInfo, error) {
	return Stat(path)
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(path string) (os.FileInfo, error) {
	return os.Stat(path)
}

// Move renames (moves) <src> to <dst> path.
func Move(src string, dst string) error {
	return os.Rename(src, dst)
}

// Alias of Move.
// See Move.
func Rename(src string, dst string) error {
	return Move(src, dst)
}

// DirNames returns sub-file names of given directory <path>.
// Note that the returned names are NOT absolute paths.
func DirNames(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdirnames(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	return list, nil
}

// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
func Glob(pattern string, onlyNames ...bool) ([]string, error) {
	if list, err := filepath.Glob(pattern); err == nil {
		if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 {
			array := make([]string, len(list))
			for k, v := range list {
				array[k] = Basename(v)
			}
			return array, nil
		}
		return list, nil
	} else {
		return nil, err
	}
}

// Remove deletes all file/directory with <path> parameter.
// If parameter <path> is directory, it deletes it recursively.
func Remove(path string) error {
	//intlog.Print(`Remove:`, path)
	return os.RemoveAll(path)
}

// IsReadable checks whether given <path> is readable.
func IsReadable(path string) bool {
	result := true
	file, err := os.OpenFile(path, os.O_RDONLY, DefaultPerm)
	if err != nil {
		result = false
	}
	file.Close()
	return result
}

// IsWritable checks whether given <path> is writable.
//
// TODO improve performance; use golang.org/x/sys to cross-plat-form
func IsWritable(path string) bool {
	result := true
	if IsDir(path) {
		// If it's a directory, create a temporary file to test whether it's writable.
		tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
		if f, err := Create(tmpFile); err != nil || !Exists(tmpFile) {
			result = false
		} else {
			f.Close()
			Remove(tmpFile)
		}
	} else {
		// 如果是文件，那么判断文件是否可打开
		file, err := os.OpenFile(path, os.O_WRONLY, DefaultPerm)
		if err != nil {
			result = false
		}
		file.Close()
	}
	return result
}

// See os.Chmod.
func Chmod(path string, mode os.FileMode) error {
	return os.Chmod(path, mode)
}

// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path. The absolute
// path name for a given file is not guaranteed to be unique.
// Abs calls Clean on the result.
func Abs(path string) string {
	p, _ := filepath.Abs(path)
	return p
}

// RealPath converts the given <path> to its absolute path
// and checks if the file path exists.
// If the file does not exist, return an empty string.
func RealPath(path string) string {
	p, err := filepath.Abs(path)
	if err != nil {
		return ""
	}
	if !Exists(p) {
		return ""
	}
	return p
}

// SelfPath returns absolute file path of current running process(binary).
func SelfPath() string {
	return selfPath
}

// SelfName returns file name of current running process(binary).
func SelfName() string {
	return Basename(SelfPath())
}

// SelfDir returns absolute directory path of current running process(binary).
func SelfDir() string {
	return filepath.Dir(SelfPath())
}

// Basename returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Basename returns a single separator.
func Basename(path string) string {
	return filepath.Base(path)
}

// Name returns the last element of path without extension.
func Name(path string) string {
	base := filepath.Base(path)
	if i := strings.LastIndexByte(base, '.'); i != -1 {
		return base[:i]
	}
	return base
}

// Dir returns all but the last element of path, typically the path's directory.
// After dropping the final element, Dir calls Clean on the path and trailing
// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
func Dir(path string) string {
	return filepath.Dir(path)
}

// IsEmpty checks whether the given <path> is empty.
// If <path> is a folder, it checks if there's any file under it.
// If <path> is a file, it checks if the file size is zero.
//
// Note that it returns true if <path> does not exist.
func IsEmpty(path string) bool {
	stat, err := Stat(path)
	if err != nil {
		return true
	}
	if stat.IsDir() {
		file, err := os.Open(path)
		if err != nil {
			return true
		}
		defer file.Close()
		names, err := file.Readdirnames(-1)
		if err != nil {
			return true
		}
		return len(names) == 0
	} else {
		return stat.Size() == 0
	}
}

// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
//
// Note: the result contains symbol '.'.
func Ext(path string) string {
	ext := filepath.Ext(path)
	if p := strings.IndexByte(ext, '?'); p != -1 {
		ext = ext[0:p]
	}
	return ext
}

// ExtName is like function Ext, which returns the file name extension used by path,
// but the result does not contains symbol '.'.
func ExtName(path string) string {
	return strings.TrimLeft(Ext(path), ".")
}

// Home returns absolute path of current user's home directory.
func Home() (string, error) {
	u, err := user.Current()
	if nil == err {
		return u.HomeDir, nil
	}
	if "windows" == runtime.GOOS {
		return homeWindows()
	}
	return homeUnix()
}

func homeUnix() (string, error) {
	if home := os.Getenv("HOME"); home != "" {
		return home, nil
	}
	var stdout bytes.Buffer
	cmd := exec.Command("sh", "-c", "eval echo ~$USER")
	cmd.Stdout = &stdout
	if err := cmd.Run(); err != nil {
		return "", err
	}

	result := strings.TrimSpace(stdout.String())
	if result == "" {
		return "", errors.New("blank output when reading home directory")
	}

	return result, nil
}

func homeWindows() (string, error) {
	drive := os.Getenv("HOMEDRIVE")
	path := os.Getenv("HOMEPATH")
	home := drive + path
	if drive == "" || path == "" {
		home = os.Getenv("USERPROFILE")
	}
	if home == "" {
		return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
	}

	return home, nil
}

// See os.TempDir().
func TempDir() string {
	return tempDir
}
<commit_msg>improve package gfile<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package gfile provides easy-to-use operations for file system.
package gfile

import (
	"bytes"
	"errors"
	"github.com/gogf/gf/text/gstr"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/util/gconv"
)

const (
	// Separator for file system.
	Separator = string(filepath.Separator)
)

var (
	// Default perm for file opening.
	DefaultPerm = os.FileMode(0666)

	// The absolute file path for main package.
	// It can be only checked and set once.
	mainPkgPath = gtype.NewString()

	// selfPath is the current running binary path.
	// As it is most commonly used, it is so defined as an internal package variable.
	selfPath = ""

	// Temporary directory of system.
	tempDir = "/tmp"
)

func init() {
	// Initialize internal package variable: tempDir.
	if !Exists(tempDir) {
		tempDir = os.TempDir()
	}
	// Initialize internal package variable: selfPath.
	selfPath, _ = exec.LookPath(os.Args[0])
	if selfPath != "" {
		selfPath, _ = filepath.Abs(selfPath)
	}
	if selfPath == "" {
		selfPath, _ = filepath.Abs(os.Args[0])
	}
}

// Mkdir creates directories recursively with given <path>.
// The parameter <path> is suggested to be absolute path.
func Mkdir(path string) error {
	err := os.MkdirAll(path, os.ModePerm)
	if err != nil {
		return err
	}
	return nil
}

// Create creates file with given <path> recursively.
// The parameter <path> is suggested to be absolute path.
func Create(path string) (*os.File, error) {
	dir := Dir(path)
	if !Exists(dir) {
		if err := Mkdir(dir); err != nil {
			return nil, err
		}
	}
	return os.Create(path)
}

// Open opens file/directory READONLY.
func Open(path string) (*os.File, error) {
	return os.Open(path)
}

// OpenFile opens file/directory with custom <flag> and <perm>.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
	return os.OpenFile(path, flag, perm)
}

// OpenWithFlag opens file/directory with default perm and custom <flag>.
// The default <perm> is 0666.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
func OpenWithFlag(path string, flag int) (*os.File, error) {
	f, err := os.OpenFile(path, flag, DefaultPerm)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// OpenWithFlagPerm opens file/directory with custom <flag> and <perm>.
// The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
// The parameter <perm> is like: 0600, 0666, 0777, etc.
func OpenWithFlagPerm(path string, flag int, perm os.FileMode) (*os.File, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// Join joins string array paths with file separator of current system.
func Join(paths ...string) string {
	var s string
	for _, path := range paths {
		if s != "" {
			s += Separator
		}
		s += gstr.TrimRight(path, Separator)
	}
	return s
}

// Exists checks whether given <path> exist.
func Exists(path string) bool {
	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
		return true
	}
	return false
}

// IsDir checks whether given <path> a directory.
func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return s.IsDir()
}

// Pwd returns absolute path of current working directory.
func Pwd() string {
	path, _ := os.Getwd()
	return path
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	return os.Chdir(dir)
}

// IsFile checks whether given <path> a file, which means it's not a directory.
func IsFile(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return !s.IsDir()
}

// Alias of Stat.
// See Stat.
func Info(path string) (os.FileInfo, error) {
	return Stat(path)
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(path string) (os.FileInfo, error) {
	return os.Stat(path)
}

// Move renames (moves) <src> to <dst> path.
func Move(src string, dst string) error {
	return os.Rename(src, dst)
}

// Alias of Move.
// See Move.
func Rename(src string, dst string) error {
	return Move(src, dst)
}

// DirNames returns sub-file names of given directory <path>.
// Note that the returned names are NOT absolute paths.
func DirNames(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdirnames(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	return list, nil
}

// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
func Glob(pattern string, onlyNames ...bool) ([]string, error) {
	if list, err := filepath.Glob(pattern); err == nil {
		if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 {
			array := make([]string, len(list))
			for k, v := range list {
				array[k] = Basename(v)
			}
			return array, nil
		}
		return list, nil
	} else {
		return nil, err
	}
}

// Remove deletes all file/directory with <path> parameter.
// If parameter <path> is directory, it deletes it recursively.
func Remove(path string) error {
	//intlog.Print(`Remove:`, path)
	return os.RemoveAll(path)
}

// IsReadable checks whether given <path> is readable.
func IsReadable(path string) bool {
	result := true
	file, err := os.OpenFile(path, os.O_RDONLY, DefaultPerm)
	if err != nil {
		result = false
	}
	file.Close()
	return result
}

// IsWritable checks whether given <path> is writable.
//
// TODO improve performance; use golang.org/x/sys to cross-plat-form
func IsWritable(path string) bool {
	result := true
	if IsDir(path) {
		// If it's a directory, create a temporary file to test whether it's writable.
		tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
		if f, err := Create(tmpFile); err != nil || !Exists(tmpFile) {
			result = false
		} else {
			f.Close()
			Remove(tmpFile)
		}
	} else {
		// 如果是文件，那么判断文件是否可打开
		file, err := os.OpenFile(path, os.O_WRONLY, DefaultPerm)
		if err != nil {
			result = false
		}
		file.Close()
	}
	return result
}

// See os.Chmod.
func Chmod(path string, mode os.FileMode) error {
	return os.Chmod(path, mode)
}

// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path. The absolute
// path name for a given file is not guaranteed to be unique.
// Abs calls Clean on the result.
func Abs(path string) string {
	p, _ := filepath.Abs(path)
	return p
}

// RealPath converts the given <path> to its absolute path
// and checks if the file path exists.
// If the file does not exist, return an empty string.
func RealPath(path string) string {
	p, err := filepath.Abs(path)
	if err != nil {
		return ""
	}
	if !Exists(p) {
		return ""
	}
	return p
}

// SelfPath returns absolute file path of current running process(binary).
func SelfPath() string {
	return selfPath
}

// SelfName returns file name of current running process(binary).
func SelfName() string {
	return Basename(SelfPath())
}

// SelfDir returns absolute directory path of current running process(binary).
func SelfDir() string {
	return filepath.Dir(SelfPath())
}

// Basename returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Basename returns a single separator.
func Basename(path string) string {
	return filepath.Base(path)
}

// Name returns the last element of path without extension.
func Name(path string) string {
	base := filepath.Base(path)
	if i := strings.LastIndexByte(base, '.'); i != -1 {
		return base[:i]
	}
	return base
}

// Dir returns all but the last element of path, typically the path's directory.
// After dropping the final element, Dir calls Clean on the path and trailing
// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
func Dir(path string) string {
	return filepath.Dir(path)
}

// IsEmpty checks whether the given <path> is empty.
// If <path> is a folder, it checks if there's any file under it.
// If <path> is a file, it checks if the file size is zero.
//
// Note that it returns true if <path> does not exist.
func IsEmpty(path string) bool {
	stat, err := Stat(path)
	if err != nil {
		return true
	}
	if stat.IsDir() {
		file, err := os.Open(path)
		if err != nil {
			return true
		}
		defer file.Close()
		names, err := file.Readdirnames(-1)
		if err != nil {
			return true
		}
		return len(names) == 0
	} else {
		return stat.Size() == 0
	}
}

// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
//
// Note: the result contains symbol '.'.
func Ext(path string) string {
	ext := filepath.Ext(path)
	if p := strings.IndexByte(ext, '?'); p != -1 {
		ext = ext[0:p]
	}
	return ext
}

// ExtName is like function Ext, which returns the file name extension used by path,
// but the result does not contains symbol '.'.
func ExtName(path string) string {
	return strings.TrimLeft(Ext(path), ".")
}

// Home returns absolute path of current user's home directory.
func Home() (string, error) {
	u, err := user.Current()
	if nil == err {
		return u.HomeDir, nil
	}
	if "windows" == runtime.GOOS {
		return homeWindows()
	}
	return homeUnix()
}

func homeUnix() (string, error) {
	if home := os.Getenv("HOME"); home != "" {
		return home, nil
	}
	var stdout bytes.Buffer
	cmd := exec.Command("sh", "-c", "eval echo ~$USER")
	cmd.Stdout = &stdout
	if err := cmd.Run(); err != nil {
		return "", err
	}

	result := strings.TrimSpace(stdout.String())
	if result == "" {
		return "", errors.New("blank output when reading home directory")
	}

	return result, nil
}

func homeWindows() (string, error) {
	drive := os.Getenv("HOMEDRIVE")
	path := os.Getenv("HOMEPATH")
	home := drive + path
	if drive == "" || path == "" {
		home = os.Getenv("USERPROFILE")
	}
	if home == "" {
		return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
	}

	return home, nil
}

// See os.TempDir().
func TempDir() string {
	return tempDir
}
<|endoftext|>


<commit_before>package otto

// StyledUi is a wrapper around an existing UI that automatically
// adds formatting around the UI text.
type StyledUi struct {
	Ui
}

func (u *StyledUi) Header(msg string) {
	u.Ui.Header("==> " + msg)
}

func (u *StyledUi) Message(msg string) {
	u.Ui.Message("    " + msg)
}
<commit_msg>otto: StyledUi properly indents all lines<commit_after>package otto

import (
	"bufio"
	"bytes"
	"strings"
	"unicode"
)

// StyledUi is a wrapper around an existing UI that automatically
// adds formatting around the UI text.
type StyledUi struct {
	Ui
}

func (u *StyledUi) Header(msg string) {
	u.Ui.Header(u.prefix("==> ", msg))
}

func (u *StyledUi) Message(msg string) {
	u.Ui.Message(u.prefix("    ", msg))
}

func (u *StyledUi) prefix(prefix, msg string) string {
	var buf bytes.Buffer

	scan := bufio.NewScanner(strings.NewReader(msg))
	for scan.Scan() {
		buf.WriteString(prefix)
		buf.WriteString(scan.Text() + "\n")
	}

	return strings.TrimRightFunc(buf.String(), unicode.IsSpace)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package factory_test

import (
	"runtime"
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/container"
	"github.com/juju/juju/container/factory"
	"github.com/juju/juju/container/lxd"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/testing"
)

type factorySuite struct {
	testing.BaseSuite
}

var _ = gc.Suite(&factorySuite{})

func (*factorySuite) TestNewContainerManager(c *gc.C) {
	for _, test := range []struct {
		containerType instance.ContainerType
		valid         bool
	}{{
		containerType: instance.LXC,
		valid:         true,
	}, {
		containerType: instance.LXD,
		valid:         true,
	}, {
		containerType: instance.KVM,
		valid:         true,
	}, {
		containerType: instance.NONE,
		valid:         false,
	}, {
		containerType: instance.ContainerType("other"),
		valid:         false,
	}} {
		/* LXD isn't available in go 1.2 */
		if lxd.HasLXDSupport() {
			continue
		}

		conf := container.ManagerConfig{container.ConfigName: "test"}
		manager, err := factory.NewContainerManager(test.containerType, conf, nil)
		if test.valid {
			c.Assert(err, jc.ErrorIsNil)
			c.Assert(manager, gc.NotNil)
		} else {
			c.Assert(err, gc.ErrorMatches, `unknown container type: ".*"`)
			c.Assert(manager, gc.IsNil)
		}
	}
}
<commit_msg>lxd: fix typo in tests<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package factory_test

import (
	"runtime"
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/container"
	"github.com/juju/juju/container/factory"
	"github.com/juju/juju/container/lxd"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/testing"
)

type factorySuite struct {
	testing.BaseSuite
}

var _ = gc.Suite(&factorySuite{})

func (*factorySuite) TestNewContainerManager(c *gc.C) {
	for _, test := range []struct {
		containerType instance.ContainerType
		valid         bool
	}{{
		containerType: instance.LXC,
		valid:         true,
	}, {
		containerType: instance.LXD,
		valid:         true,
	}, {
		containerType: instance.KVM,
		valid:         true,
	}, {
		containerType: instance.NONE,
		valid:         false,
	}, {
		containerType: instance.ContainerType("other"),
		valid:         false,
	}} {
		/* LXD isn't available in go 1.2 */
		if !lxd.HasLXDSupport() {
			continue
		}

		conf := container.ManagerConfig{container.ConfigName: "test"}
		manager, err := factory.NewContainerManager(test.containerType, conf, nil)
		if test.valid {
			c.Assert(err, jc.ErrorIsNil)
			c.Assert(manager, gc.NotNil)
		} else {
			c.Assert(err, gc.ErrorMatches, `unknown container type: ".*"`)
			c.Assert(manager, gc.IsNil)
		}
	}
}
<|endoftext|>