<commit_before>package req

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"math"
	urlpkg "net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

type ParallelDownload struct {
	url          string
	client       *Client
	concurrency  int
	output       io.Writer
	filename     string
	segmentSize  int64
	perm         os.FileMode
	cacheRootDir string
	cacheDir     string
	taskCh       chan *downloadTask
	doneCh       chan struct{}
	wgDoneCh     chan struct{}
	errCh        chan error
	wg           sync.WaitGroup
	taskMap      map[int]*downloadTask
	taskNotifyCh chan *downloadTask
	mu           sync.Mutex
	lastIndex    int
}

func (pd *ParallelDownload) completeTask(task *downloadTask) {
	pd.mu.Lock()
	pd.taskMap[task.index] = task
	pd.mu.Unlock()
	pd.taskNotifyCh <- task
}

func (pd *ParallelDownload) popTask(index int) *downloadTask {
	pd.mu.Lock()
	if task, ok := pd.taskMap[index]; ok {
		delete(pd.taskMap, index)
		pd.mu.Unlock()
		return task
	}
	pd.mu.Unlock()
	for {
		task := <-pd.taskNotifyCh
		if task.index == index {
			pd.mu.Lock()
			delete(pd.taskMap, index)
			pd.mu.Unlock()
			return task
		}
	}
}

func md5Sum(s string) string {
	sum := md5.Sum([]byte(s))
	return hex.EncodeToString(sum[:])
}

func (pd *ParallelDownload) ensure() error {
	if pd.concurrency <= 0 {
		pd.concurrency = 5
	}
	if pd.segmentSize <= 0 {
		pd.segmentSize = 1073741824 // 10MB
	}
	if pd.perm == 0 {
		pd.perm = 0777
	}
	if pd.cacheRootDir == "" {
		pd.cacheRootDir = os.TempDir()
	}
	if pd.client.DebugLog {
		pd.client.log.Debugf("use cache root directory %s", pd.cacheRootDir)
		pd.client.log.Debugf("download with %d concurrency and %d bytes segment size", pd.concurrency, pd.segmentSize)
	}
	pd.cacheDir = filepath.Join(pd.cacheRootDir, md5Sum(pd.url))
	err := os.MkdirAll(pd.cacheDir, os.ModePerm)
	if err != nil {
		return err
	}

	pd.taskCh = make(chan *downloadTask)
	pd.doneCh = make(chan struct{})
	pd.wgDoneCh = make(chan struct{})
	pd.errCh = make(chan error)
	pd.taskMap = make(map[int]*downloadTask)
	pd.taskNotifyCh = make(chan *downloadTask)
	return nil
}

func (pd *ParallelDownload) SetSegmentSize(segmentSize int64) *ParallelDownload {
	pd.segmentSize = segmentSize
	return pd
}

func (pd *ParallelDownload) SetCacheRootDir(cacheRootDir string) *ParallelDownload {
	pd.cacheRootDir = cacheRootDir
	return pd
}

func (pd *ParallelDownload) SetFileMode(perm os.FileMode) *ParallelDownload {
	pd.perm = perm
	return pd
}

func (pd *ParallelDownload) SetConcurrency(concurrency int) *ParallelDownload {
	pd.concurrency = concurrency
	return pd
}

func (pd *ParallelDownload) SetOutput(output io.Writer) *ParallelDownload {
	if output != nil {
		pd.output = output
	}
	return pd
}

func (pd *ParallelDownload) SetOutputFile(filename string) *ParallelDownload {
	pd.filename = filename
	return pd
}

func getRangeTempFile(rangeStart, rangeEnd int64, workerDir string) string {
	return filepath.Join(workerDir, fmt.Sprintf("temp-%d-%d", rangeStart, rangeEnd))
}

type downloadTask struct {
	index                int
	rangeStart, rangeEnd int64
	tempFilename         string
	tempFile             *os.File
}

func (pd *ParallelDownload) handleTask(t *downloadTask) {
	pd.wg.Add(1)
	defer pd.wg.Done()
	t.tempFilename = getRangeTempFile(t.rangeStart, t.rangeEnd, pd.cacheDir)
	if pd.client.DebugLog {
		pd.client.log.Debugf("downloading segment %d-%d", t.rangeStart, t.rangeEnd)
	}
	file, err := os.OpenFile(t.tempFilename, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		pd.errCh <- err
		return
	}
	_, err = pd.client.R().
		SetHeader("Range", fmt.Sprintf("bytes=%d-%d", t.rangeStart, t.rangeEnd)).
		SetOutput(file).
		Get(pd.url)
	if err != nil {
		pd.errCh <- err
		return
	}
	t.tempFile = file
	pd.completeTask(t)
}

func (pd *ParallelDownload) startWorker() {
	for {
		select {
		case t := <-pd.taskCh:
			pd.handleTask(t)
		case <-pd.doneCh:
			return
		}
	}
}

func (pd *ParallelDownload) mergeFile() {
	defer pd.wg.Done()
	file, err := pd.getOutputFile()
	if err != nil {
		pd.errCh <- err
		return
	}
	for i := 0; ; i++ {
		task := pd.popTask(i)
		tempFile, err := os.Open(task.tempFilename)
		if err != nil {
			pd.errCh <- err
			return
		}
		_, err = io.Copy(file, tempFile)
		tempFile.Close()
		if err != nil {
			pd.errCh <- err
			return
		}
		if i < pd.lastIndex {
			continue
		}
		break
	}
	err = os.RemoveAll(pd.cacheDir)
	if err != nil {
		pd.errCh <- err
	}
	if pd.client.DebugLog {
		pd.client.log.Debugf("removed cache directory %s", pd.cacheDir)
	}
}

func (pd *ParallelDownload) Do() error {
	err := pd.ensure()
	if err != nil {
		return err
	}
	for i := 0; i < pd.concurrency; i++ {
		go pd.startWorker()
	}
	resp, err := pd.client.R().Head(pd.url)
	if err != nil {
		return err
	}
	if resp.ContentLength <= 0 {
		return fmt.Errorf("bad content length: %d", resp.ContentLength)
	}
	pd.lastIndex = int(math.Ceil(float64(resp.ContentLength)/float64(pd.segmentSize))) - 1
	pd.wg.Add(1)
	go pd.mergeFile()
	go func() {
		pd.wg.Wait()
		close(pd.wgDoneCh)
	}()
	totalBytes := resp.ContentLength
	start := int64(0)
	for i := 0; ; i++ {
		end := start + (pd.segmentSize - 1)
		if end > (totalBytes - 1) {
			end = totalBytes - 1
		}
		task := &downloadTask{
			index:      i,
			rangeStart: start,
			rangeEnd:   end,
		}
		pd.taskCh <- task
		if end < (totalBytes - 1) {
			start = end + 1
			continue
		}
		break
	}
	select {
	case <-pd.wgDoneCh:
		if pd.client.DebugLog {
			if pd.filename != "" {
				pd.client.log.Debugf("download completed from %s to %s", pd.url, pd.filename)
			} else {
				pd.client.log.Debugf("download completed for %s", pd.url)
			}
		}
		close(pd.doneCh)
	case err := <-pd.errCh:
		return err
	}
	return nil
}

func (pd *ParallelDownload) getOutputFile() (io.Writer, error) {
	outputFile := pd.output
	if outputFile != nil {
		return outputFile, nil
	}
	if pd.filename == "" {
		u, err := urlpkg.Parse(pd.url)
		if err != nil {
			panic(err)
		}
		paths := strings.Split(u.Path, "/")
		for i := len(paths) - 1; i > 0; i-- {
			if paths[i] != "" {
				pd.filename = paths[i]
				break
			}
		}
		if pd.filename == "" {
			pd.filename = "download"
		}
	}
	if pd.client.outputDirectory != "" && !filepath.IsAbs(pd.filename) {
		pd.filename = filepath.Join(pd.client.outputDirectory, pd.filename)
	}
	return os.OpenFile(pd.filename, os.O_RDWR|os.O_CREATE, pd.perm)
}
<commit_msg>optimize parallel download<commit_after>package req

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"math"
	urlpkg "net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

type ParallelDownload struct {
	url          string
	client       *Client
	concurrency  int
	output       io.Writer
	filename     string
	segmentSize  int64
	perm         os.FileMode
	cacheRootDir string
	cacheDir     string
	taskCh       chan *downloadTask
	doneCh       chan struct{}
	wgDoneCh     chan struct{}
	errCh        chan error
	wg           sync.WaitGroup
	taskMap      map[int]*downloadTask
	taskNotifyCh chan *downloadTask
	mu           sync.Mutex
	lastIndex    int
}

func (pd *ParallelDownload) completeTask(task *downloadTask) {
	pd.mu.Lock()
	pd.taskMap[task.index] = task
	pd.mu.Unlock()
	go func() {
		select {
		case pd.taskNotifyCh <- task:
		case <-pd.doneCh:
		}
	}()
}

func (pd *ParallelDownload) popTask(index int) *downloadTask {
	pd.mu.Lock()
	if task, ok := pd.taskMap[index]; ok {
		delete(pd.taskMap, index)
		pd.mu.Unlock()
		return task
	}
	pd.mu.Unlock()
	for {
		task := <-pd.taskNotifyCh
		if task.index == index {
			pd.mu.Lock()
			delete(pd.taskMap, index)
			pd.mu.Unlock()
			return task
		}
	}
}

func md5Sum(s string) string {
	sum := md5.Sum([]byte(s))
	return hex.EncodeToString(sum[:])
}

func (pd *ParallelDownload) ensure() error {
	if pd.concurrency <= 0 {
		pd.concurrency = 5
	}
	if pd.segmentSize <= 0 {
		pd.segmentSize = 1073741824 // 10MB
	}
	if pd.perm == 0 {
		pd.perm = 0777
	}
	if pd.cacheRootDir == "" {
		pd.cacheRootDir = os.TempDir()
	}
	pd.cacheDir = filepath.Join(pd.cacheRootDir, md5Sum(pd.url))
	if pd.client.DebugLog {
		pd.client.log.Debugf("use cache directory %s", pd.cacheDir)
		pd.client.log.Debugf("download with %d concurrency and %d bytes segment size", pd.concurrency, pd.segmentSize)
	}
	err := os.MkdirAll(pd.cacheDir, os.ModePerm)
	if err != nil {
		return err
	}

	pd.taskCh = make(chan *downloadTask)
	pd.doneCh = make(chan struct{})
	pd.wgDoneCh = make(chan struct{})
	pd.errCh = make(chan error)
	pd.taskMap = make(map[int]*downloadTask)
	pd.taskNotifyCh = make(chan *downloadTask)
	return nil
}

func (pd *ParallelDownload) SetSegmentSize(segmentSize int64) *ParallelDownload {
	pd.segmentSize = segmentSize
	return pd
}

func (pd *ParallelDownload) SetCacheRootDir(cacheRootDir string) *ParallelDownload {
	pd.cacheRootDir = cacheRootDir
	return pd
}

func (pd *ParallelDownload) SetFileMode(perm os.FileMode) *ParallelDownload {
	pd.perm = perm
	return pd
}

func (pd *ParallelDownload) SetConcurrency(concurrency int) *ParallelDownload {
	pd.concurrency = concurrency
	return pd
}

func (pd *ParallelDownload) SetOutput(output io.Writer) *ParallelDownload {
	if output != nil {
		pd.output = output
	}
	return pd
}

func (pd *ParallelDownload) SetOutputFile(filename string) *ParallelDownload {
	pd.filename = filename
	return pd
}

func getRangeTempFile(rangeStart, rangeEnd int64, workerDir string) string {
	return filepath.Join(workerDir, fmt.Sprintf("temp-%d-%d", rangeStart, rangeEnd))
}

type downloadTask struct {
	index                int
	rangeStart, rangeEnd int64
	tempFilename         string
	tempFile             *os.File
}

func (pd *ParallelDownload) handleTask(t *downloadTask) {
	pd.wg.Add(1)
	defer pd.wg.Done()
	t.tempFilename = getRangeTempFile(t.rangeStart, t.rangeEnd, pd.cacheDir)
	if pd.client.DebugLog {
		pd.client.log.Debugf("downloading segment %d-%d", t.rangeStart, t.rangeEnd)
	}
	file, err := os.OpenFile(t.tempFilename, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		pd.errCh <- err
		return
	}
	_, err = pd.client.R().
		SetHeader("Range", fmt.Sprintf("bytes=%d-%d", t.rangeStart, t.rangeEnd)).
		SetOutput(file).
		Get(pd.url)
	if err != nil {
		pd.errCh <- err
		return
	}
	t.tempFile = file
	pd.completeTask(t)
}

func (pd *ParallelDownload) startWorker() {
	for {
		select {
		case t := <-pd.taskCh:
			pd.handleTask(t)
		case <-pd.doneCh:
			return
		}
	}
}

func (pd *ParallelDownload) mergeFile() {
	defer pd.wg.Done()
	file, err := pd.getOutputFile()
	if err != nil {
		pd.errCh <- err
		return
	}
	for i := 0; ; i++ {
		task := pd.popTask(i)
		tempFile, err := os.Open(task.tempFilename)
		if err != nil {
			pd.errCh <- err
			return
		}
		_, err = io.Copy(file, tempFile)
		tempFile.Close()
		if err != nil {
			pd.errCh <- err
			return
		}
		if i < pd.lastIndex {
			continue
		}
		break
	}
	if pd.client.DebugLog {
		pd.client.log.Debugf("removing cache directory %s", pd.cacheDir)
	}
	err = os.RemoveAll(pd.cacheDir)
	if err != nil {
		pd.errCh <- err
	}
}

func (pd *ParallelDownload) Do() error {
	err := pd.ensure()
	if err != nil {
		return err
	}
	for i := 0; i < pd.concurrency; i++ {
		go pd.startWorker()
	}
	resp, err := pd.client.R().Head(pd.url)
	if err != nil {
		return err
	}
	if resp.ContentLength <= 0 {
		return fmt.Errorf("bad content length: %d", resp.ContentLength)
	}
	pd.lastIndex = int(math.Ceil(float64(resp.ContentLength)/float64(pd.segmentSize))) - 1
	pd.wg.Add(1)
	go pd.mergeFile()
	go func() {
		pd.wg.Wait()
		close(pd.wgDoneCh)
	}()
	totalBytes := resp.ContentLength
	start := int64(0)
	for i := 0; ; i++ {
		end := start + (pd.segmentSize - 1)
		if end > (totalBytes - 1) {
			end = totalBytes - 1
		}
		task := &downloadTask{
			index:      i,
			rangeStart: start,
			rangeEnd:   end,
		}
		pd.taskCh <- task
		if end < (totalBytes - 1) {
			start = end + 1
			continue
		}
		break
	}
	select {
	case <-pd.wgDoneCh:
		if pd.client.DebugLog {
			if pd.filename != "" {
				pd.client.log.Debugf("download completed from %s to %s", pd.url, pd.filename)
			} else {
				pd.client.log.Debugf("download completed for %s", pd.url)
			}
		}
		close(pd.doneCh)
	case err := <-pd.errCh:
		return err
	}
	return nil
}

func (pd *ParallelDownload) getOutputFile() (io.Writer, error) {
	outputFile := pd.output
	if outputFile != nil {
		return outputFile, nil
	}
	if pd.filename == "" {
		u, err := urlpkg.Parse(pd.url)
		if err != nil {
			panic(err)
		}
		paths := strings.Split(u.Path, "/")
		for i := len(paths) - 1; i > 0; i-- {
			if paths[i] != "" {
				pd.filename = paths[i]
				break
			}
		}
		if pd.filename == "" {
			pd.filename = "download"
		}
	}
	if pd.client.outputDirectory != "" && !filepath.IsAbs(pd.filename) {
		pd.filename = filepath.Join(pd.client.outputDirectory, pd.filename)
	}
	return os.OpenFile(pd.filename, os.O_RDWR|os.O_CREATE, pd.perm)
}
<|endoftext|>


<commit_before>package parser

import (
	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/scanner"
)

var valueTypes = []scanner.TokenType{
	scanner.TokenTypeBoolean,
	scanner.TokenTypeNumber,
	scanner.TokenTypeFloat,
	scanner.TokenTypeString,
}

var unaryPrefix = []scanner.TokenType{
	scanner.TokenTypeADD,
	scanner.TokenTypeSUB,
	scanner.TokenTypeIncrement,
	scanner.TokenTypeDecrement,
	scanner.TokenTypeEXCL,
}

var unarySuffix = []scanner.TokenType{
	scanner.TokenTypeIncrement,
	scanner.TokenTypeDecrement,
}

func (p *Parser) parseMemberExpression(target ast.Expression) (node *ast.MemberExpression, ok bool) {
	_, ok = p.expectToken(scanner.TokenTypePERIOD)
	if !ok {
		p.unread()
		return
	}

	token, propertyOk := p.expectToken(scanner.TokenTypeIdent)
	if !propertyOk {
		p.error(unexpected(token.StringValue(), "property name"))
		return
	}

	if isKeyword(token.Text) {
		p.error(reservedKeywordError(token))
		return
	}

	node = &ast.MemberExpression{
		Target:   target,
		Property: token,
	}

	return
}

func (p *Parser) parseCallExpression(target ast.Expression) (node *ast.FunctionCall, ok bool) {
	_, ok = p.expectToken(scanner.TokenTypeLPAREN)
	if !ok {
		p.unread()
		return
	}

	args := make([]*ast.CallArgument, 0)
	for {
		arg, ok := p.parseCallArgument()
		if !ok {
			break
		}

		args = append(args, arg)
		_, commaOk := p.expectToken(scanner.TokenTypeCOMMA)
		if !commaOk {
			p.unread()
			break
		}
	}

	token, rParenOk := p.expectToken(scanner.TokenTypeRPAREN)
	if !rParenOk {
		p.error(unexpectedToken(token, scanner.TokenTypeRPAREN))
		return
	}

	node = &ast.FunctionCall{
		Callee:    target,
		Arguments: args,
		End:       ast.EndPositionFromToken(token),
	}

	return
}

func (p *Parser) parseCallArgument() (arg *ast.CallArgument, ok bool) {
	arg = &ast.CallArgument{}
	p.snapshot()
	tokens, namedArgument := p.expectPattern(scanner.TokenTypeIdent, scanner.TokenTypeCOLON)
	if namedArgument {
		p.commit()
		if isKeyword(tokens[0].Text) {
			p.error(reservedKeywordError(tokens[0]))
			return
		}
		arg.Name = &ast.Identifier{Token: tokens[0]}
	} else {
		p.restore()
	}

	expr, ok := p.parseExpression()
	if ok {
		arg.Expression = expr
		p.checkCommentForNode(arg, true)
	}

	return
}

func (p *Parser) parseValueExpression() (expression ast.Expression, ok bool) {
	var token scanner.Token
	if token, ok = p.expectToken(valueTypes...); !ok {
		p.unread()
		return
	}
	return &ast.ValueExpression{Token: token}, true
}

func (p *Parser) parseIdentfier() (expression *ast.Identifier, ok bool) {
	var token scanner.Token
	if token, ok = p.expectToken(scanner.TokenTypeIdent); !ok {
		p.unread()
		return
	}

	expression = &ast.Identifier{Token: token}

	if isKeyword(token.Text) {
		p.error(reservedKeywordError(token))
		return
	}

	return
}

func (p *Parser) parseUnaryExpression() (expression ast.Expression, ok bool) {
	token, prefixOk := p.expectToken(unaryPrefix...)
	if prefixOk {
		var rExpr ast.Expression
		rExpr, ok = p.parseUnaryExpression()
		if !ok {
			p.error(unexpected(p.read().StringValue(), "expression"))
			return
		}
		expression = &ast.UnaryExpression{
			Operator:   token,
			Expression: rExpr,
		}
		return
	}

	p.unread()

	check := func(expr ast.Expression, cok bool) bool {
		if cok {
			ok = cok
			expression = expr
		}

		return cok
	}

	switch {
	case check(p.parseParenExpressionOrTuple()):
	case check(p.parseFuncDecl()):
	case check(p.parseArrayExpression()):
	case check(p.parseIdentfier()):
	case check(p.parseValueExpression()):
	// case check(p.parseBlock()): this messes up for loops
	case check(p.parseMacroSubstitutionExpression()):
	default:
		return
	}

rightLoop:
	for {
		// Parse function calls, member expressions and type casts
		switch {
		case check(p.parseAssigment(expression)):
		case check(p.parseCallExpression(expression)):
		case check(p.parseMemberExpression(expression)):
		case check(p.parseComparisonExpression(expression)):
		default:
			break rightLoop
		}
	}

	if ok {
		token, suffixOk := p.expectToken(unarySuffix...)
		if suffixOk {
			expression = &ast.UnaryExpression{
				Operator:   token,
				Expression: expression,
				Postfix:    true,
			}
			return
		}

		p.unread()
	}

	return
}

func (p *Parser) parseBinaryExpression(left ast.Expression) (node *ast.BinaryExpression, ok bool) {
	token, ok := p.expectToken(
		scanner.TokenTypeADD,
		scanner.TokenTypeSUB,
		scanner.TokenTypeASTERISK,
		scanner.TokenTypeSLASH,
	)

	if !ok {
		p.unread()
		return
	}

	var right ast.Expression
	var exprOk bool
	right, exprOk = p.parseUnaryExpression()
	if !exprOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	if nextToken, nextTokenOk := p.expectToken(
		scanner.TokenTypeADD,
		scanner.TokenTypeSUB,
		scanner.TokenTypeASTERISK,
		scanner.TokenTypeSLASH,
	); nextTokenOk {
		p.unread()
		// TODO use proper weights
		if nextToken.Type == scanner.TokenTypeASTERISK || nextToken.Type == scanner.TokenTypeSLASH {
			right, exprOk = p.parseBinaryExpression(right)
			if !exprOk {
				p.error(unexpected(p.read().StringValue(), "expression"))
				return
			}
		} else {
			return p.parseBinaryExpression(&ast.BinaryExpression{
				Operator: token,
				Left:     left,
				Right:    right,
			})
		}
	} else {
		p.unread()
	}

	node = &ast.BinaryExpression{
		Operator: token,
		Left:     left,
		Right:    right,
	}

	return
}

func (p *Parser) parseExpression() (expression ast.Expression, ok bool) {
	if expression, ok = p.parseUnaryExpression(); ok {
		if binaryExpression, binaryOk := p.parseBinaryExpression(expression); binaryOk {
			expression = binaryExpression
		}
	}
	return
}

func (p *Parser) parseComparisonExpression(left ast.Expression) (node ast.Expression, ok bool) {
	token, ok := p.expectToken(
		scanner.TokenTypeEqual,
		scanner.TokenTypeNotEqual,
		scanner.TokenTypeLess,
		scanner.TokenTypeGreater,
		scanner.TokenTypeLessOrEqual,
		scanner.TokenTypeGreaterOrEqual,
	)

	if !ok {
		p.unread()
		return
	}

	right, expressionOk := p.parseExpression()
	if !expressionOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	node = &ast.ComparisonExpression{
		Left:     left,
		Right:    right,
		Operator: token,
	}

	return
}

func (p *Parser) parseExpressionList() (expressions []ast.Expression, ok bool) {

	for {
		if expr, exprOk := p.parseExpression(); exprOk {
			ok = true
			expressions = append(expressions, expr)
		} else {
			if len(expressions) > 0 {
				ok = false
				token := p.read()
				p.error(unexpected(token.StringValue(), "expression"))
			}
			break
		}

		_, commaOK := p.expectToken(scanner.TokenTypeCOMMA)
		if !commaOK {
			p.unread()
			break
		}
	}

	return
}

func (p *Parser) parseParenExpressionOrTuple() (node ast.Expression, ok bool) {
	leftToken, leftTokenOk := p.expectToken(scanner.TokenTypeLPAREN)
	if !leftTokenOk {
		p.unread()
		return
	}

	exprList, exprListOk := p.parseExpressionList()
	if !exprListOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	rightToken, rightTokenOk := p.expectToken(scanner.TokenTypeRPAREN)
	if !rightTokenOk {
		p.error(unexpectedToken(rightToken, scanner.TokenTypeRPAREN))
		return
	}

	if len(exprList) == 1 {
		ok = true
		node = &ast.ParenExpression{
			LeftParen:  leftToken,
			RightParen: rightToken,
			Expression: exprList[0],
		}
	} else {
		ok = true
		node = &ast.TupleExpression{
			LeftParen:   leftToken,
			RightParen:  rightToken,
			Expressions: exprList,
		}
	}

	return
}

func (p *Parser) parseArrayExpression() (node ast.Expression, ok bool) {
	typ, typOk := p.parseArrayType()
	if !typOk {
		return
	}

	lBrace, lBraceOk := p.expectToken(scanner.TokenTypeLBRACE)
	if !lBraceOk {
		p.error(unexpectedToken(lBrace, scanner.TokenTypeLBRACE))
		return
	}

	expresList, exprListOk := p.parseExpressionList()

	rBrace, rBraceOk := p.expectToken(scanner.TokenTypeRBRACE)
	if !rBraceOk {
		if !exprListOk {
			p.error(unexpected(rBrace.StringValue(), "expression list or left brace"))
			return
		}
		p.error(unexpectedToken(rBrace, scanner.TokenTypeRBRACE))
		return
	}

	ok = true
	node = &ast.ArrayExpression{
		RightBrace:  rBrace,
		LeftBrace:   lBrace,
		Expressions: expresList,
		Type:        typ.(*ast.ArrayType),
	}

	return
}
<commit_msg>Fix precedence<commit_after>package parser

import (
	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/scanner"
)

var valueTypes = []scanner.TokenType{
	scanner.TokenTypeBoolean,
	scanner.TokenTypeNumber,
	scanner.TokenTypeFloat,
	scanner.TokenTypeString,
}

var unaryPrefix = []scanner.TokenType{
	scanner.TokenTypeADD,
	scanner.TokenTypeSUB,
	scanner.TokenTypeIncrement,
	scanner.TokenTypeDecrement,
	scanner.TokenTypeEXCL,
}

var unarySuffix = []scanner.TokenType{
	scanner.TokenTypeIncrement,
	scanner.TokenTypeDecrement,
}

func (p *Parser) parseMemberExpression(target ast.Expression) (node *ast.MemberExpression, ok bool) {
	_, ok = p.expectToken(scanner.TokenTypePERIOD)
	if !ok {
		p.unread()
		return
	}

	token, propertyOk := p.expectToken(scanner.TokenTypeIdent)
	if !propertyOk {
		p.error(unexpected(token.StringValue(), "property name"))
		return
	}

	if isKeyword(token.Text) {
		p.error(reservedKeywordError(token))
		return
	}

	node = &ast.MemberExpression{
		Target:   target,
		Property: token,
	}

	return
}

func (p *Parser) parseCallExpression(target ast.Expression) (node *ast.FunctionCall, ok bool) {
	_, ok = p.expectToken(scanner.TokenTypeLPAREN)
	if !ok {
		p.unread()
		return
	}

	args := make([]*ast.CallArgument, 0)
	for {
		arg, ok := p.parseCallArgument()
		if !ok {
			break
		}

		args = append(args, arg)
		_, commaOk := p.expectToken(scanner.TokenTypeCOMMA)
		if !commaOk {
			p.unread()
			break
		}
	}

	token, rParenOk := p.expectToken(scanner.TokenTypeRPAREN)
	if !rParenOk {
		p.error(unexpectedToken(token, scanner.TokenTypeRPAREN))
		return
	}

	node = &ast.FunctionCall{
		Callee:    target,
		Arguments: args,
		End:       ast.EndPositionFromToken(token),
	}

	return
}

func (p *Parser) parseCallArgument() (arg *ast.CallArgument, ok bool) {
	arg = &ast.CallArgument{}
	p.snapshot()
	tokens, namedArgument := p.expectPattern(scanner.TokenTypeIdent, scanner.TokenTypeCOLON)
	if namedArgument {
		p.commit()
		if isKeyword(tokens[0].Text) {
			p.error(reservedKeywordError(tokens[0]))
			return
		}
		arg.Name = &ast.Identifier{Token: tokens[0]}
	} else {
		p.restore()
	}

	expr, ok := p.parseExpression()
	if ok {
		arg.Expression = expr
		p.checkCommentForNode(arg, true)
	}

	return
}

func (p *Parser) parseValueExpression() (expression ast.Expression, ok bool) {
	var token scanner.Token
	if token, ok = p.expectToken(valueTypes...); !ok {
		p.unread()
		return
	}
	return &ast.ValueExpression{Token: token}, true
}

func (p *Parser) parseIdentfier() (expression *ast.Identifier, ok bool) {
	var token scanner.Token
	if token, ok = p.expectToken(scanner.TokenTypeIdent); !ok {
		p.unread()
		return
	}

	expression = &ast.Identifier{Token: token}

	if isKeyword(token.Text) {
		p.error(reservedKeywordError(token))
		return
	}

	return
}

func (p *Parser) parseUnaryExpression() (expression ast.Expression, ok bool) {
	token, prefixOk := p.expectToken(unaryPrefix...)
	if prefixOk {
		var rExpr ast.Expression
		rExpr, ok = p.parseUnaryExpression()
		if !ok {
			p.error(unexpected(p.read().StringValue(), "expression"))
			return
		}
		expression = &ast.UnaryExpression{
			Operator:   token,
			Expression: rExpr,
		}
		return
	}

	p.unread()

	check := func(expr ast.Expression, cok bool) bool {
		if cok {
			ok = cok
			expression = expr
		}

		return cok
	}

	switch {
	case check(p.parseParenExpressionOrTuple()):
	case check(p.parseFuncDecl()):
	case check(p.parseArrayExpression()):
	case check(p.parseIdentfier()):
	case check(p.parseValueExpression()):
	// case check(p.parseBlock()): this messes up for loops
	case check(p.parseMacroSubstitutionExpression()):
	default:
		return
	}

rightLoop:
	for {
		// Parse function calls, member expressions and type casts
		switch {
		case check(p.parseAssigment(expression)):
		case check(p.parseCallExpression(expression)):
		case check(p.parseMemberExpression(expression)):
		case check(p.parseComparisonExpression(expression)):
		default:
			break rightLoop
		}
	}

	if ok {
		token, suffixOk := p.expectToken(unarySuffix...)
		if suffixOk {
			expression = &ast.UnaryExpression{
				Operator:   token,
				Expression: expression,
				Postfix:    true,
			}
			return
		}

		p.unread()
	}

	return
}

func (p *Parser) parseBinaryExpression(left ast.Expression) (node *ast.BinaryExpression, ok bool) {
	token, ok := p.expectToken(
		scanner.TokenTypeADD,
		scanner.TokenTypeSUB,
		scanner.TokenTypeASTERISK,
		scanner.TokenTypeSLASH,
	)

	if !ok {
		p.unread()
		return
	}

	var right ast.Expression
	var exprOk bool
	right, exprOk = p.parseUnaryExpression()
	if !exprOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	if nextToken, nextTokenOk := p.expectToken(
		scanner.TokenTypeADD,
		scanner.TokenTypeSUB,
		scanner.TokenTypeASTERISK,
		scanner.TokenTypeSLASH,
	); nextTokenOk {
		p.unread()
		// TODO use proper weights
		if !(token.Type == scanner.TokenTypeASTERISK || token.Type == scanner.TokenTypeSLASH) && (nextToken.Type == scanner.TokenTypeASTERISK || nextToken.Type == scanner.TokenTypeSLASH) {
			right, exprOk = p.parseBinaryExpression(right)
			if !exprOk {
				p.error(unexpected(p.read().StringValue(), "expression"))
				return
			}
		} else {
			return p.parseBinaryExpression(&ast.BinaryExpression{
				Operator: token,
				Left:     left,
				Right:    right,
			})
		}
	} else {
		p.unread()
	}

	node = &ast.BinaryExpression{
		Operator: token,
		Left:     left,
		Right:    right,
	}

	return
}

func (p *Parser) parseExpression() (expression ast.Expression, ok bool) {
	if expression, ok = p.parseUnaryExpression(); ok {
		if binaryExpression, binaryOk := p.parseBinaryExpression(expression); binaryOk {
			expression = binaryExpression
		}
	}
	return
}

func (p *Parser) parseComparisonExpression(left ast.Expression) (node ast.Expression, ok bool) {
	token, ok := p.expectToken(
		scanner.TokenTypeEqual,
		scanner.TokenTypeNotEqual,
		scanner.TokenTypeLess,
		scanner.TokenTypeGreater,
		scanner.TokenTypeLessOrEqual,
		scanner.TokenTypeGreaterOrEqual,
	)

	if !ok {
		p.unread()
		return
	}

	right, expressionOk := p.parseExpression()
	if !expressionOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	node = &ast.ComparisonExpression{
		Left:     left,
		Right:    right,
		Operator: token,
	}

	return
}

func (p *Parser) parseExpressionList() (expressions []ast.Expression, ok bool) {

	for {
		if expr, exprOk := p.parseExpression(); exprOk {
			ok = true
			expressions = append(expressions, expr)
		} else {
			if len(expressions) > 0 {
				ok = false
				token := p.read()
				p.error(unexpected(token.StringValue(), "expression"))
			}
			break
		}

		_, commaOK := p.expectToken(scanner.TokenTypeCOMMA)
		if !commaOK {
			p.unread()
			break
		}
	}

	return
}

func (p *Parser) parseParenExpressionOrTuple() (node ast.Expression, ok bool) {
	leftToken, leftTokenOk := p.expectToken(scanner.TokenTypeLPAREN)
	if !leftTokenOk {
		p.unread()
		return
	}

	exprList, exprListOk := p.parseExpressionList()
	if !exprListOk {
		p.error(unexpected(p.read().StringValue(), "expression"))
		return
	}

	rightToken, rightTokenOk := p.expectToken(scanner.TokenTypeRPAREN)
	if !rightTokenOk {
		p.error(unexpectedToken(rightToken, scanner.TokenTypeRPAREN))
		return
	}

	if len(exprList) == 1 {
		ok = true
		node = &ast.ParenExpression{
			LeftParen:  leftToken,
			RightParen: rightToken,
			Expression: exprList[0],
		}
	} else {
		ok = true
		node = &ast.TupleExpression{
			LeftParen:   leftToken,
			RightParen:  rightToken,
			Expressions: exprList,
		}
	}

	return
}

func (p *Parser) parseArrayExpression() (node ast.Expression, ok bool) {
	typ, typOk := p.parseArrayType()
	if !typOk {
		return
	}

	lBrace, lBraceOk := p.expectToken(scanner.TokenTypeLBRACE)
	if !lBraceOk {
		p.error(unexpectedToken(lBrace, scanner.TokenTypeLBRACE))
		return
	}

	expresList, exprListOk := p.parseExpressionList()

	rBrace, rBraceOk := p.expectToken(scanner.TokenTypeRBRACE)
	if !rBraceOk {
		if !exprListOk {
			p.error(unexpected(rBrace.StringValue(), "expression list or left brace"))
			return
		}
		p.error(unexpectedToken(rBrace, scanner.TokenTypeRBRACE))
		return
	}

	ok = true
	node = &ast.ArrayExpression{
		RightBrace:  rBrace,
		LeftBrace:   lBrace,
		Expressions: expresList,
		Type:        typ.(*ast.ArrayType),
	}

	return
}
<|endoftext|>


<commit_before>package parser

import (
	"testing"

	"github.com/fatih/color"
)

var items = map[itemType]string{
	itemVersion:  "itemVersion",
	itemOperator: "itemOperator",
	itemSet:      "itemSet",
	itemRange:    "itemRange",
	itemAdvanced: "itemAdvanced",
	itemError:    "itemError",
	itemEOF:      "itemEOF",
}

type results []itemType

type lexerTestables struct {
	expected bool
	value    string
	result   results
}

var constraints = []*lexerTestables{
	{true, "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3",
		results{
			itemVersion,
			itemRange,
			itemOperator,
			itemVersion,
			itemRange,
			itemVersion,
			itemAdvanced,
			itemVersion,
		},
	},
	{true, "~1.2.3",
		results{itemAdvanced, itemVersion},
	},
	{true, "^4.5.2-alpha.1",
		results{itemAdvanced, itemVersion},
	},
	{true, "=2.3.2",
		results{itemOperator, itemVersion},
	},
	{true, "<=1.2.3",
		results{itemOperator, itemVersion},
	},
	{true, "5.3.5||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 ||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5|| 4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 4.3.5",
		results{itemVersion, itemSet, itemVersion},
	},
	{true, ">=1.2.3",
		results{itemOperator, itemVersion},
	},
	//
	{false, "~ 1.2.3",
		results{itemAdvanced},
	},
	{false, ">= 1.2.3",
		results{},
	},
	{false, "1.2.3 >=",
		results{itemVersion, itemSet},
	},
	{false, "5.3.5 |1| 4.3.5",
		results{itemVersion},
	},
	{false, "5. 4.4",
		results{},
	},
	{false, "<1<1",
		results{itemOperator},
	},
	{false, "<1||",
		results{itemOperator, itemVersion},
	},
	{false, "M",
		results{},
	},
	{true, "1.0",
		results{itemAdvanced},
	},
	{true, "1.x",
		results{itemAdvanced},
	},
	{false, "1.x+98uihuhyg",
		results{},
	},
	{true, "1.*.2",
		results{itemAdvanced},
	},
	{true, "1.*.2-beta",
		results{itemAdvanced},
	},
	{true, "*.1.2",
		results{itemAdvanced},
	},
	{false, "1x.2.*",
		results{},
	},
	{false, "1.x2.*",
		results{},
	},
	{false, "1...1",
		results{},
	},
	{false, "1.x.",
		results{},
	},
}

func init() {
	// Appends appropriate end token based on expected result.
	for _, c := range constraints {
		if c.expected {
			c.result = append(c.result, itemEOF)
		} else {
			c.result = append(c.result, itemError)
		}
	}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()

func TestLexer(t *testing.T) {
	for _, c := range constraints {
		_, ch := lex(c.value)
		result := true
		x := 0
		for i := range ch {

			result = (i.typ != itemError)

			if i.typ != c.result[x] {
				t.Logf("lex(%v) => %v, want %v \n", cyan(c.value), items[i.typ], items[c.result[x]])
				t.Logf("lex(%v) => %v: %v \n", cyan(c.value), items[i.typ], yellow(i))
			}
			x++
		}
		if result != c.expected {
			t.Logf("lex(%v) => %t, want %t \n", cyan(c.value), result, c.expected)
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerComplex(b *testing.B) {
	const VERSION = "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3 || ~4.3.1 ^2.1.1"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerSimple(b *testing.B) {
	const VERSION = "1.0.0"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}
<commit_msg>Joined the error messages into one.<commit_after>package parser

import (
	"testing"

	"github.com/fatih/color"
)

var items = map[itemType]string{
	itemVersion:  "itemVersion",
	itemOperator: "itemOperator",
	itemSet:      "itemSet",
	itemRange:    "itemRange",
	itemAdvanced: "itemAdvanced",
	itemError:    "itemError",
	itemEOF:      "itemEOF",
}

type results []itemType

type lexerTestables struct {
	expected bool
	value    string
	result   results
}

var constraints = []*lexerTestables{
	{true, "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3",
		results{
			itemVersion,
			itemRange,
			itemOperator,
			itemVersion,
			itemRange,
			itemVersion,
			itemAdvanced,
			itemVersion,
		},
	},
	{true, "~1.2.3",
		results{itemAdvanced, itemVersion},
	},
	{true, "^4.5.2-alpha.1",
		results{itemAdvanced, itemVersion},
	},
	{true, "=2.3.2",
		results{itemOperator, itemVersion},
	},
	{true, "<=1.2.3",
		results{itemOperator, itemVersion},
	},
	{true, "5.3.5||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 ||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5|| 4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 4.3.5",
		results{itemVersion, itemSet, itemVersion},
	},
	{true, ">=1.2.3",
		results{itemOperator, itemVersion},
	},
	//
	{false, "~ 1.2.3",
		results{itemAdvanced},
	},
	{false, ">= 1.2.3",
		results{},
	},
	{false, "1.2.3 >=",
		results{itemVersion, itemSet},
	},
	{false, "5.3.5 |1| 4.3.5",
		results{itemVersion},
	},
	{false, "5. 4.4",
		results{},
	},
	{false, "<1<1",
		results{itemOperator},
	},
	{false, "<1||",
		results{itemOperator, itemVersion},
	},
	{false, "M",
		results{},
	},
	{true, "1.0",
		results{itemAdvanced},
	},
	{true, "1.x",
		results{itemAdvanced},
	},
	{false, "1.x+98uihuhyg",
		results{},
	},
	{true, "1.*.2",
		results{itemAdvanced},
	},
	{true, "1.*.2-beta",
		results{itemAdvanced},
	},
	{true, "*.1.2",
		results{itemAdvanced},
	},
	{false, "1x.2.*",
		results{},
	},
	{false, "1.x2.*",
		results{},
	},
	{false, "1...1",
		results{},
	},
	{false, "1.x.",
		results{},
	},
}

func init() {
	// Appends appropriate end token based on expected result.
	for _, c := range constraints {
		if c.expected {
			c.result = append(c.result, itemEOF)
		} else {
			c.result = append(c.result, itemError)
		}
	}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()

func TestLexer(t *testing.T) {
	for _, c := range constraints {
		_, ch := lex(c.value)
		result := true
		x := 0
		for i := range ch {

			result = (i.typ != itemError)

			if i.typ != c.result[x] {
				t.Logf("lex(%v) => %v(%v), want %v \n", cyan(c.value), items[i.typ], yellow(i), items[c.result[x]])
			}
			x++
		}
		if result != c.expected {
			t.Logf("lex(%v) => %t, want %t \n", cyan(c.value), result, c.expected)
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerComplex(b *testing.B) {
	const VERSION = "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3 || ~4.3.1 ^2.1.1"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerSimple(b *testing.B) {
	const VERSION = "1.0.0"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>package parser

import (
	"strings"

	"modernc.org/cc"
)

type TargetArch string

const (
	Arch32    TargetArch = "i386"
	Arch48    TargetArch = "x86_48"
	Arch64    TargetArch = "x86_64"
	ArchArm32 TargetArch = "arm"
	ArchArm64 TargetArch = "aarch64"
)

var builtinBase = `
#define __builtin_va_list void *
#define __asm(x)
#define __inline
#define __inline__
#define __signed
#define __signed__
#define __const const
#define __extension__
#define __attribute__(x)
#define __attribute(x)
#define __restrict
#define __volatile__

#define __builtin_inff() (0)
#define __builtin_infl() (0)
#define __builtin_inf() (0)
#define __builtin_fabsf(x) (0)
#define __builtin_fabsl(x) (0)
#define __builtin_fabs(x) (0)

#define __INTRINSIC_PROLOG(name)
`

var builtinBaseUndef = `
#undef __llvm__
#undef __BLOCKS__
`

var basePredefines = `
#define __STDC_HOSTED__ 1
#define __STDC_VERSION__ 199901L
#define __STDC__ 1
#define __GNUC__ 4
#define __GNUC_PREREQ(maj,min) 0
#define __POSIX_C_DEPRECATED(ver)

#define __FLT_MIN__ 0
#define __DBL_MIN__ 0
#define __LDBL_MIN__ 0

void __GO__(char*, ...);
`

var archPredefines = map[TargetArch]string{
	Arch32: `#define __i386__ 1`,
	Arch48: `#define __x86_64__ 1`,
	Arch64: `#define __x86_64__ 1`,
	ArchArm32: strings.Join([]string{
		`#define __ARM_EABI__ 1`,
		`#define __arm__ 1`,
	}, "\n"),
	ArchArm64: strings.Join([]string{
		`#define __ARM_EABI__ 1`,
		`#define __aarch64__ 1`,
	}, "\n"),
	// TODO(xlab): https://sourceforge.net/p/predef/wiki/Architectures/
}

var models = map[TargetArch]*cc.Model{
	Arch32:    model32,
	Arch48:    model48,
	Arch64:    model64,
	ArchArm32: model32,
	ArchArm64: model64,
}

var arches = map[string]TargetArch{
	"386":         Arch32,
	"arm":         ArchArm32,
	"aarch64":     ArchArm64,
	"armv7a":      ArchArm32,
	"armv8a":      ArchArm64,
	"armeabi-v7a": ArchArm32,
	"armeabi-v8a": ArchArm64,
	"armbe":       ArchArm32,
	"mips":        Arch32,
	"mipsle":      Arch32,
	"sparc":       Arch32,
	"amd64":       Arch64,
	"amd64p32":    ArchArm32,
	"arm64":       ArchArm64,
	"arm64be":     ArchArm64,
	"ppc64":       Arch64,
	"ppc64le":     Arch64,
	"mips64":      Arch64,
	"mips64le":    Arch64,
	"mips64p32":   Arch48,
	"mips64p32le": Arch48,
	"sparc64":     Arch64,
}

var model32 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {4, 4, 4, "__TODO_PTR"},
		cc.UintPtr:           {4, 4, 4, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {4, 4, 4, "int32"},
		cc.ULong:             {4, 4, 4, "uint32"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}

var model48 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {4, 4, 4, "__TODO_PTR"},
		cc.UintPtr:           {4, 4, 4, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {8, 8, 8, "int64"},
		cc.ULong:             {8, 8, 8, "uint64"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}

var model64 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {8, 8, 8, "__TODO_PTR"},
		cc.UintPtr:           {8, 8, 8, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {8, 8, 8, "int64"},
		cc.ULong:             {8, 8, 8, "uint64"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}
<commit_msg>Support latest macOS Clang tooling by overriding __has_include_next macro.<commit_after>package parser

import (
	"strings"

	"modernc.org/cc"
)

type TargetArch string

const (
	Arch32    TargetArch = "i386"
	Arch48    TargetArch = "x86_48"
	Arch64    TargetArch = "x86_64"
	ArchArm32 TargetArch = "arm"
	ArchArm64 TargetArch = "aarch64"
)

var builtinBase = `
#define __builtin_va_list void *
#define __asm(x)
#define __inline
#define __inline__
#define __signed
#define __signed__
#define __const const
#define __extension__
#define __attribute__(x)
#define __attribute(x)
#define __restrict
#define __volatile__

#define __builtin_inff() (0)
#define __builtin_infl() (0)
#define __builtin_inf() (0)
#define __builtin_fabsf(x) (0)
#define __builtin_fabsl(x) (0)
#define __builtin_fabs(x) (0)

#define __INTRINSIC_PROLOG(name)
`

var builtinBaseUndef = `
#undef __llvm__
#undef __BLOCKS__
`

var basePredefines = `
#define __STDC_HOSTED__ 1
#define __STDC_VERSION__ 199901L
#define __STDC__ 1
#define __GNUC__ 4
#define __GNUC_PREREQ(maj,min) 0
#define __POSIX_C_DEPRECATED(ver)
#define __has_include_next(...) 1

#define __FLT_MIN__ 0
#define __DBL_MIN__ 0
#define __LDBL_MIN__ 0

void __GO__(char*, ...);
`

var archPredefines = map[TargetArch]string{
	Arch32: `#define __i386__ 1`,
	Arch48: `#define __x86_64__ 1`,
	Arch64: `#define __x86_64__ 1`,
	ArchArm32: strings.Join([]string{
		`#define __ARM_EABI__ 1`,
		`#define __arm__ 1`,
	}, "\n"),
	ArchArm64: strings.Join([]string{
		`#define __ARM_EABI__ 1`,
		`#define __aarch64__ 1`,
	}, "\n"),
	// TODO(xlab): https://sourceforge.net/p/predef/wiki/Architectures/
}

var models = map[TargetArch]*cc.Model{
	Arch32:    model32,
	Arch48:    model48,
	Arch64:    model64,
	ArchArm32: model32,
	ArchArm64: model64,
}

var arches = map[string]TargetArch{
	"386":         Arch32,
	"arm":         ArchArm32,
	"aarch64":     ArchArm64,
	"armv7a":      ArchArm32,
	"armv8a":      ArchArm64,
	"armeabi-v7a": ArchArm32,
	"armeabi-v8a": ArchArm64,
	"armbe":       ArchArm32,
	"mips":        Arch32,
	"mipsle":      Arch32,
	"sparc":       Arch32,
	"amd64":       Arch64,
	"amd64p32":    ArchArm32,
	"arm64":       ArchArm64,
	"arm64be":     ArchArm64,
	"ppc64":       Arch64,
	"ppc64le":     Arch64,
	"mips64":      Arch64,
	"mips64le":    Arch64,
	"mips64p32":   Arch48,
	"mips64p32le": Arch48,
	"sparc64":     Arch64,
}

var model32 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {4, 4, 4, "__TODO_PTR"},
		cc.UintPtr:           {4, 4, 4, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {4, 4, 4, "int32"},
		cc.ULong:             {4, 4, 4, "uint32"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}

var model48 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {4, 4, 4, "__TODO_PTR"},
		cc.UintPtr:           {4, 4, 4, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {8, 8, 8, "int64"},
		cc.ULong:             {8, 8, 8, "uint64"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}

var model64 = &cc.Model{
	Items: map[cc.Kind]cc.ModelItem{
		cc.Ptr:               {8, 8, 8, "__TODO_PTR"},
		cc.UintPtr:           {8, 8, 8, "uintptr"},
		cc.Void:              {0, 1, 1, "__TODO_VOID"},
		cc.Char:              {1, 1, 1, "int8"},
		cc.SChar:             {1, 1, 1, "int8"},
		cc.UChar:             {1, 1, 1, "byte"},
		cc.Short:             {2, 2, 2, "int16"},
		cc.UShort:            {2, 2, 2, "uint16"},
		cc.Int:               {4, 4, 4, "int32"},
		cc.UInt:              {4, 4, 4, "uint32"},
		cc.Long:              {8, 8, 8, "int64"},
		cc.ULong:             {8, 8, 8, "uint64"},
		cc.LongLong:          {8, 8, 8, "int64"},
		cc.ULongLong:         {8, 8, 8, "uint64"},
		cc.Float:             {4, 4, 4, "float32"},
		cc.Double:            {8, 8, 4, "float64"},
		cc.LongDouble:        {8, 8, 4, "float64"},
		cc.Bool:              {1, 1, 1, "bool"},
		cc.FloatComplex:      {8, 8, 8, "complex64"},
		cc.DoubleComplex:     {16, 16, 16, "complex128"},
		cc.LongDoubleComplex: {16, 16, 16, "complex128"},
	},
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/apex/log"
	"os"
	"path"
)

// Defines basic system configuration settings.
type SystemConfiguration struct {
	// The root directory where all of the pterodactyl data is stored at.
	RootDirectory string `default:"/var/lib/pterodactyl" yaml:"root_directory"`

	// Directory where logs for server installations and other wings events are logged.
	LogDirectory string `default:"/var/log/pterodactyl" yaml:"log_directory"`

	// Directory where the server data is stored at.
	Data string `default:"/var/lib/pterodactyl/volumes" yaml:"data"`

	// Directory where server archives for transferring will be stored.
	ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" yaml:"archive_directory"`

	// Directory where local backups will be stored on the machine.
	BackupDirectory string `default:"/var/lib/pterodactyl/backups" yaml:"backup_directory"`

	// The user that should own all of the server files, and be used for containers.
	Username string `default:"pterodactyl" yaml:"username"`

	// Definitions for the user that gets created to ensure that we can quickly access
	// this information without constantly having to do a system lookup.
	User struct {
		Uid int
		Gid int
	}

	// Determines if Wings should detect a server that stops with a normal exit code of
	// "0" as being crashed if the process stopped without any Wings interaction. E.g.
	// the user did not press the stop button, but the process stopped cleanly.
	DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`

	// If set to true, file permissions for a server will be checked when the process is
	// booted. This can cause boot delays if the server has a large amount of files. In most
	// cases disabling this should not have any major impact unless external processes are
	// frequently modifying a servers' files.
	CheckPermissionsOnBoot bool `default:"true" yaml:"check_permissions_on_boot"`

	Sftp SftpConfiguration `yaml:"sftp"`
}

// Ensures that all of the system directories exist on the system. These directories are
// created so that only the owner can read the data, and no other users.
func (sc *SystemConfiguration) ConfigureDirectories() error {
	log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists")
	if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
	if err := os.MkdirAll(sc.Data, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists")
	if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists")
	if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil {
		return err
	}

	return nil
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetStatesPath() string {
	return path.Join(sc.RootDirectory, "states.json")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetInstallLogPath() string {
	return path.Join(sc.LogDirectory, "install/")
}
<commit_msg>Update data path if the server data directory is a symlink<commit_after>package config

import (
	"github.com/apex/log"
	"github.com/pkg/errors"
	"os"
	"path"
	"path/filepath"
)

// Defines basic system configuration settings.
type SystemConfiguration struct {
	// The root directory where all of the pterodactyl data is stored at.
	RootDirectory string `default:"/var/lib/pterodactyl" yaml:"root_directory"`

	// Directory where logs for server installations and other wings events are logged.
	LogDirectory string `default:"/var/log/pterodactyl" yaml:"log_directory"`

	// Directory where the server data is stored at.
	Data string `default:"/var/lib/pterodactyl/volumes" yaml:"data"`

	// Directory where server archives for transferring will be stored.
	ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" yaml:"archive_directory"`

	// Directory where local backups will be stored on the machine.
	BackupDirectory string `default:"/var/lib/pterodactyl/backups" yaml:"backup_directory"`

	// The user that should own all of the server files, and be used for containers.
	Username string `default:"pterodactyl" yaml:"username"`

	// Definitions for the user that gets created to ensure that we can quickly access
	// this information without constantly having to do a system lookup.
	User struct {
		Uid int
		Gid int
	}

	// Determines if Wings should detect a server that stops with a normal exit code of
	// "0" as being crashed if the process stopped without any Wings interaction. E.g.
	// the user did not press the stop button, but the process stopped cleanly.
	DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`

	// If set to true, file permissions for a server will be checked when the process is
	// booted. This can cause boot delays if the server has a large amount of files. In most
	// cases disabling this should not have any major impact unless external processes are
	// frequently modifying a servers' files.
	CheckPermissionsOnBoot bool `default:"true" yaml:"check_permissions_on_boot"`

	Sftp SftpConfiguration `yaml:"sftp"`
}

// Ensures that all of the system directories exist on the system. These directories are
// created so that only the owner can read the data, and no other users.
func (sc *SystemConfiguration) ConfigureDirectories() error {
	log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists")
	if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil {
		return err
	}

	// There are a non-trivial number of users out there whose data directories are actually a
	// symlink to another location on the disk. If we do not resolve that final destination at this
	// point things will appear to work, but endless errors whill be encountered when we try to
	// verify accessed paths since they will all end up resolving outside the expected data directory.
	//
	// For the sake of automating away as much of this as possible, see if the data directory is a
	// symlink, and if so resolve to its final real path, and then update the configuration to use
	// that.
	if d, err := filepath.EvalSymlinks(sc.Data); err != nil {
		if !os.IsNotExist(err) {
			return errors.WithStack(err)
		}
	} else if d != sc.Data {
		sc.Data = d
	}

	log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
	if err := os.MkdirAll(sc.Data, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists")
	if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists")
	if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil {
		return err
	}

	return nil
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetStatesPath() string {
	return path.Join(sc.RootDirectory, "states.json")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetInstallLogPath() string {
	return path.Join(sc.LogDirectory, "install/")
}
<|endoftext|>


<commit_before>package schema

import (
	"testing"

	"github.com/golang/protobuf/proto"
)

func TestMergeConfiguration(t *testing.T) {
	merge, expect := GetDefaultConfiguration(), GetDefaultConfiguration()
	MergeConfiguration(merge, nil)

	if !proto.Equal(merge, expect) {
		t.Error("not equal", merge, expect)
	}
}

func TestMergeConfiguration_setsAll(t *testing.T) {
	merge, expect := new(Configuration), GetDefaultConfiguration()
	MergeConfiguration(merge, GetDefaultConfiguration())

	if !proto.Equal(merge, expect) {
		t.Error("not equal", merge, expect)
	}
}
<commit_msg>schema: add test to show all fields are set<commit_after>package schema

import (
	"reflect"
	"testing"

	"github.com/golang/protobuf/proto"
)

func TestMergeConfiguration(t *testing.T) {
	merge, expect := GetDefaultConfiguration(), GetDefaultConfiguration()
	MergeConfiguration(merge, nil)

	if !proto.Equal(merge, expect) {
		t.Error("not equal", merge, expect)
	}
}

func TestMergeConfiguration_setsAll(t *testing.T) {
	merge, expect := new(Configuration), GetDefaultConfiguration()
	MergeConfiguration(merge, GetDefaultConfiguration())

	if !proto.Equal(merge, expect) {
		t.Error("not equal", merge, expect)
	}
}

func TestConfiguration_AllFieldsSet(t *testing.T) {
	conf := *GetDefaultConfiguration()
	val := reflect.ValueOf(conf)
	for i := 0; i < val.NumField(); i++ {
		if _, present := val.Type().Field(i).Tag.Lookup("protobuf"); !present {
			continue
		}
		f := val.Field(i)
		switch f.Kind() {
		case reflect.Ptr:
		default:
			t.Fatal(val.Type().Field(i).Name)
		}
		if f.IsNil() {
			t.Error("unset field", val.Type().Field(i).Name)
		}
	}
}
<|endoftext|>


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

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must have
// at least one certificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerHostname or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *listener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must have
// at least one certificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || len(config.Certificates) == 0 {
		return nil, errors.New("tls.Listen: no certificates in configuration")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) {
	var certDERBlock *pem.Block
	for {
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		}
	}

	if len(cert.Certificate) == 0 {
		err = errors.New("crypto/tls: failed to parse certificate PEM data")
		return
	}

	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			err = errors.New("crypto/tls: failed to parse key PEM data")
			return
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
	}

	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			err = errors.New("crypto/tls: private key type does not match public key type")
			return
		}
		if pub.N.Cmp(priv.N) != 0 {
			err = errors.New("crypto/tls: private key does not match public key")
			return
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			err = errors.New("crypto/tls: private key type does not match public key type")
			return

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			err = errors.New("crypto/tls: private key does not match public key")
			return
		}
	default:
		err = errors.New("crypto/tls: unknown public key algorithm")
		return
	}

	return
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<commit_msg>crypto/tls: fix typo referencing the required Config field<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must have
// at least one certificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *listener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must have
// at least one certificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || len(config.Certificates) == 0 {
		return nil, errors.New("tls.Listen: no certificates in configuration")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) {
	var certDERBlock *pem.Block
	for {
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		}
	}

	if len(cert.Certificate) == 0 {
		err = errors.New("crypto/tls: failed to parse certificate PEM data")
		return
	}

	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			err = errors.New("crypto/tls: failed to parse key PEM data")
			return
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
	}

	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			err = errors.New("crypto/tls: private key type does not match public key type")
			return
		}
		if pub.N.Cmp(priv.N) != 0 {
			err = errors.New("crypto/tls: private key does not match public key")
			return
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			err = errors.New("crypto/tls: private key type does not match public key type")
			return

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			err = errors.New("crypto/tls: private key does not match public key")
			return
		}
	default:
		err = errors.New("crypto/tls: unknown public key algorithm")
		return
	}

	return
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<|endoftext|>


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

// Package build provides tools for building Go packages.
package build

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

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile("[/\\:]")

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<commit_msg>go/build: use back quotes for regular expression<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package build provides tools for building Go packages.
package build

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

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile(`[/\\:]`)

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<|endoftext|>