<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/disk"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/repr"
	"github.com/jacobsa/comeback/sys"
	"io"
	"log"
	"os"
	"path"
	"syscall"
)

const (
	gTarget = "/tmp/restore_target"
)

var blobStore blob.Store

type score struct {
	hash []byte
}

func (s *score) Sha1Hash() []byte {
	return s.hash
}

func fromHexHash(h string) (blob.Score, error) {
	b, err := hex.DecodeString(h)
	if err != nil {
		return nil, fmt.Errorf("Invalid hex string: %s", h)
	}

	return &score{b}, nil
}

func chooseUserId(uid sys.UserId, username *string) (sys.UserId, error) {
	// If there is no symbolic username, just return the UID.
	if username == nil {
		return uid, nil
	}

	// Create a user registry.
	registry, err := sys.NewUserRegistry()
	if err != nil {
		return 0, fmt.Errorf("Creating user registry: %v", err)
	}

	// Attempt to look up the username. If it's not found, return the UID.
	betterUid, err := registry.FindByName(*username)

	if _, ok := err.(sys.NotFoundError); ok {
		return uid, nil
	} else if err != nil {
		return 0, fmt.Errorf("Looking up user: %v", err)
	}

	return betterUid, nil
}

func chooseGroupId(gid sys.GroupId, groupname *string) (sys.GroupId, error) {
	// If there is no symbolic groupname, just return the GID.
	if groupname == nil {
		return gid, nil
	}

	// Create a group registry.
	registry, err := sys.NewGroupRegistry()
	if err != nil {
		return 0, fmt.Errorf("Creating group registry: %v", err)
	}

	// Attempt to look up the groupname. If it's not found, return the GID.
	betterGid, err := registry.FindByName(*groupname)

	if _, ok := err.(sys.NotFoundError); ok {
		return gid, nil
	} else if err != nil {
		return 0, fmt.Errorf("Looking up group: %v", err)
	}

	return betterGid, nil
}

// Restore the file whose contents are described by the referenced blobs to the
// supplied target, whose parent must already exist.
func restoreFile(target string, scores []blob.Score) error {
	// Open the file.
	f, err := os.Create(target)
	defer f.Close()

	if err != nil {
		return fmt.Errorf("Create: %v", err)
	}

	// Process each blob.
	for _, score := range scores {
		// Load the blob.
		blob, err := blobStore.Load(score)
		if err != nil {
			return fmt.Errorf("Loading blob: %v", err)
		}

		// Write out its contents.
		_, err = io.Copy(f, bytes.NewReader(blob))
		if err != nil {
			return fmt.Errorf("Copy: %v", err)
		}
	}

	return nil
}

// Like os.Chmod, but don't follow symlinks.
func setPermissions(path string, permissions os.FileMode) error {
	mode := syscallPermissions(permissions)

	// Open the file without following symlinks.
	fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_SYMLINK, 0)
	if err != nil {
		return err
	}

	defer syscall.Close(fd)

	// Call fchmod.
	err = syscall.Fchmod(fd, mode)
	if err != nil {
		return err
	}

	return nil
}

// Restore the directory whose contents are described by the referenced blob to
// the supplied target, which must already exist.
func restoreDir(target string, score blob.Score) error {
	// Load the appropriate blob.
	blob, err := blobStore.Load(score)
	if err != nil {
		return fmt.Errorf("Loading blob: %v", err)
	}

	// Parse its contents.
	entries, err := repr.Unmarshal(blob)
	if err != nil {
		return fmt.Errorf("Parsing blob: %v", err)
	}

	// Deal with each entry.
	for _, entry := range entries {
		entryPath := path.Join(target, entry.Name)

		// Switch on type.
		switch entry.Type {
		case fs.TypeFile:
			if err := restoreFile(entryPath, entry.Scores); err != nil {
				return err
			}

			if err := setPermissions(entryPath, entry.Permissions); err != nil {
				return err
			}

		case fs.TypeDirectory:
			if len(entry.Scores) != 1 {
				return fmt.Errorf("Wrong number of scores: %v", entry)
			}

			if err = os.Mkdir(entryPath, 0700); err != nil {
				return err
			}

			if err = restoreDir(entryPath, entry.Scores[0]); err != nil {
				return err
			}

		case fs.TypeSymlink:
			err = os.Symlink(entry.Target, entryPath)
			if err != nil {
				return err
			}

		case fs.TypeNamedPipe:
			err = makeNamedPipe(entryPath, entry.Permissions)
			if err != nil {
				return err
			}

		case fs.TypeBlockDevice:
			err = makeBlockDevice(entryPath, entry.Permissions, entry.Device)
			if err != nil {
				return err
			}

		case fs.TypeCharDevice:
			err = makeCharDevice(entryPath, entry.Permissions, entry.Device)
			if err != nil {
				return err
			}

		default:
			return fmt.Errorf("Don't know how to deal with entry: %v", entry)
		}

		// Fix ownership.
		uid, err := chooseUserId(entry.Uid, entry.Username)
		if err != nil {
			return fmt.Errorf("chooseUserId: %v", err)
		}

		gid, err := chooseGroupId(entry.Gid, entry.Groupname)
		if err != nil {
			return fmt.Errorf("chooseGroupId: %v", err)
		}

		if err = os.Chown(entryPath, int(uid), int(gid)); err != nil {
			return fmt.Errorf("Chown: %v", err)
		}
	}

	return nil
}

func syscallPermissions(permissions os.FileMode) (o uint32) {
	// Include r/w/x permission bits.
	o = uint32(permissions & os.ModePerm)

	// Also include setuid/setgid/sticky bits.
	if permissions&os.ModeSetuid != 0 {
		o |= syscall.S_ISUID
	}

	if permissions&os.ModeSetgid != 0 {
		o |= syscall.S_ISGID
	}

	if permissions&os.ModeSticky != 0 {
		o |= syscall.S_ISVTX
	}

	return
}

// Create a named pipe at the supplied path.
func makeNamedPipe(path string, permissions os.FileMode) error {
	return syscall.Mkfifo(path, syscallPermissions(permissions))
}

// Create a block device at the supplied path.
func makeBlockDevice(path string, permissions os.FileMode, dev int32) error {
	mode := syscallPermissions(permissions) | syscall.S_IFBLK
	if err := syscall.Mknod(path, mode, int(dev)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

// Create a character device at the supplied path.
func makeCharDevice(path string, permissions os.FileMode, dev int32) error {
	mode := syscallPermissions(permissions) | syscall.S_IFCHR
	if err := syscall.Mknod(path, mode, int(dev)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func main() {
	var err error

	// Create the blob store.
	blobStore, err = disk.NewBlobStore("/tmp/blobs")
	if err != nil {
		log.Fatalf("Creating store: %v", err)
	}

	// Parse the score.
	score, err := fromHexHash("228a6254c7585525744192b51f099833fca8c654")
	if err != nil {
		log.Fatalf("Parsing score: %v", err)
	}

	// Make sure the target doesn't exist.
	err = os.RemoveAll(gTarget)
	if err != nil {
		log.Fatalf("RemoveAll: %v", err)
	}

	// Create the target.
	err = os.Mkdir("/tmp/restore_target", 0755)
	if err != nil {
		log.Fatalf("Mkdir: %v", err)
	}

	// Attempt a restore.
	err = restoreDir(gTarget, score)
	if err != nil {
		log.Fatalf("Restoring: %v", err)
	}
}
<commit_msg>Fixed a bug.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/disk"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/repr"
	"github.com/jacobsa/comeback/sys"
	"io"
	"log"
	"os"
	"path"
	"syscall"
)

const (
	gTarget = "/tmp/restore_target"
)

var blobStore blob.Store

type score struct {
	hash []byte
}

func (s *score) Sha1Hash() []byte {
	return s.hash
}

func fromHexHash(h string) (blob.Score, error) {
	b, err := hex.DecodeString(h)
	if err != nil {
		return nil, fmt.Errorf("Invalid hex string: %s", h)
	}

	return &score{b}, nil
}

func chooseUserId(uid sys.UserId, username *string) (sys.UserId, error) {
	// If there is no symbolic username, just return the UID.
	if username == nil {
		return uid, nil
	}

	// Create a user registry.
	registry, err := sys.NewUserRegistry()
	if err != nil {
		return 0, fmt.Errorf("Creating user registry: %v", err)
	}

	// Attempt to look up the username. If it's not found, return the UID.
	betterUid, err := registry.FindByName(*username)

	if _, ok := err.(sys.NotFoundError); ok {
		return uid, nil
	} else if err != nil {
		return 0, fmt.Errorf("Looking up user: %v", err)
	}

	return betterUid, nil
}

func chooseGroupId(gid sys.GroupId, groupname *string) (sys.GroupId, error) {
	// If there is no symbolic groupname, just return the GID.
	if groupname == nil {
		return gid, nil
	}

	// Create a group registry.
	registry, err := sys.NewGroupRegistry()
	if err != nil {
		return 0, fmt.Errorf("Creating group registry: %v", err)
	}

	// Attempt to look up the groupname. If it's not found, return the GID.
	betterGid, err := registry.FindByName(*groupname)

	if _, ok := err.(sys.NotFoundError); ok {
		return gid, nil
	} else if err != nil {
		return 0, fmt.Errorf("Looking up group: %v", err)
	}

	return betterGid, nil
}

// Restore the file whose contents are described by the referenced blobs to the
// supplied target, whose parent must already exist.
func restoreFile(target string, scores []blob.Score) error {
	// Open the file.
	f, err := os.Create(target)
	defer f.Close()

	if err != nil {
		return fmt.Errorf("Create: %v", err)
	}

	// Process each blob.
	for _, score := range scores {
		// Load the blob.
		blob, err := blobStore.Load(score)
		if err != nil {
			return fmt.Errorf("Loading blob: %v", err)
		}

		// Write out its contents.
		_, err = io.Copy(f, bytes.NewReader(blob))
		if err != nil {
			return fmt.Errorf("Copy: %v", err)
		}
	}

	return nil
}

// Like os.Chmod, but don't follow symlinks.
func setPermissions(path string, permissions os.FileMode) error {
	mode := syscallPermissions(permissions)

	// Open the file without following symlinks.
	fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_SYMLINK, 0)
	if err != nil {
		return err
	}

	defer syscall.Close(fd)

	// Call fchmod.
	err = syscall.Fchmod(fd, mode)
	if err != nil {
		return err
	}

	return nil
}

// Restore the directory whose contents are described by the referenced blob to
// the supplied target, which must already exist.
func restoreDir(target string, score blob.Score) error {
	// Load the appropriate blob.
	blob, err := blobStore.Load(score)
	if err != nil {
		return fmt.Errorf("Loading blob: %v", err)
	}

	// Parse its contents.
	entries, err := repr.Unmarshal(blob)
	if err != nil {
		return fmt.Errorf("Parsing blob: %v", err)
	}

	// Deal with each entry.
	for _, entry := range entries {
		entryPath := path.Join(target, entry.Name)

		// Switch on type.
		switch entry.Type {
		case fs.TypeFile:
			if err := restoreFile(entryPath, entry.Scores); err != nil {
				return err
			}

			if err := setPermissions(entryPath, entry.Permissions); err != nil {
				return err
			}

		case fs.TypeDirectory:
			if len(entry.Scores) != 1 {
				return fmt.Errorf("Wrong number of scores: %v", entry)
			}

			if err = os.Mkdir(entryPath, 0700); err != nil {
				return err
			}

			if err = restoreDir(entryPath, entry.Scores[0]); err != nil {
				return err
			}

		case fs.TypeSymlink:
			err = os.Symlink(entry.Target, entryPath)
			if err != nil {
				return err
			}

		case fs.TypeNamedPipe:
			err = makeNamedPipe(entryPath, entry.Permissions)
			if err != nil {
				return err
			}

		case fs.TypeBlockDevice:
			err = makeBlockDevice(entryPath, entry.Permissions, entry.Device)
			if err != nil {
				return err
			}

		case fs.TypeCharDevice:
			err = makeCharDevice(entryPath, entry.Permissions, entry.Device)
			if err != nil {
				return err
			}

		default:
			return fmt.Errorf("Don't know how to deal with entry: %v", entry)
		}

		// Fix ownership.
		uid, err := chooseUserId(entry.Uid, entry.Username)
		if err != nil {
			return fmt.Errorf("chooseUserId: %v", err)
		}

		gid, err := chooseGroupId(entry.Gid, entry.Groupname)
		if err != nil {
			return fmt.Errorf("chooseGroupId: %v", err)
		}

		if err = os.Lchown(entryPath, int(uid), int(gid)); err != nil {
			return fmt.Errorf("Chown: %v", err)
		}
	}

	return nil
}

func syscallPermissions(permissions os.FileMode) (o uint32) {
	// Include r/w/x permission bits.
	o = uint32(permissions & os.ModePerm)

	// Also include setuid/setgid/sticky bits.
	if permissions&os.ModeSetuid != 0 {
		o |= syscall.S_ISUID
	}

	if permissions&os.ModeSetgid != 0 {
		o |= syscall.S_ISGID
	}

	if permissions&os.ModeSticky != 0 {
		o |= syscall.S_ISVTX
	}

	return
}

// Create a named pipe at the supplied path.
func makeNamedPipe(path string, permissions os.FileMode) error {
	return syscall.Mkfifo(path, syscallPermissions(permissions))
}

// Create a block device at the supplied path.
func makeBlockDevice(path string, permissions os.FileMode, dev int32) error {
	mode := syscallPermissions(permissions) | syscall.S_IFBLK
	if err := syscall.Mknod(path, mode, int(dev)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

// Create a character device at the supplied path.
func makeCharDevice(path string, permissions os.FileMode, dev int32) error {
	mode := syscallPermissions(permissions) | syscall.S_IFCHR
	if err := syscall.Mknod(path, mode, int(dev)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func main() {
	var err error

	// Create the blob store.
	blobStore, err = disk.NewBlobStore("/tmp/blobs")
	if err != nil {
		log.Fatalf("Creating store: %v", err)
	}

	// Parse the score.
	score, err := fromHexHash("228a6254c7585525744192b51f099833fca8c654")
	if err != nil {
		log.Fatalf("Parsing score: %v", err)
	}

	// Make sure the target doesn't exist.
	err = os.RemoveAll(gTarget)
	if err != nil {
		log.Fatalf("RemoveAll: %v", err)
	}

	// Create the target.
	err = os.Mkdir("/tmp/restore_target", 0755)
	if err != nil {
		log.Fatalf("Mkdir: %v", err)
	}

	// Attempt a restore.
	err = restoreDir(gTarget, score)
	if err != nil {
		log.Fatalf("Restoring: %v", err)
	}
}
<|endoftext|>


<commit_before>package mackerel

// TODO

// Channel represents a Mackerel notification channel.
// ref. https://mackerel.io/api-docs/entry/channels
type Channel struct {
	ID string `json:"id"`
	ChannelWithoutID
}

// ChannelWithoutID represents a Mackerel notification channel without the ID.
type ChannelWithoutID struct {
	Name string `json:"name"`
	Type string `json:"type"`

	// Exists when the type is "email"
	Emails  []string `json:"emails,omitempty"`
	UserIDs []string `json:"userIds,omitempty"`

	// Exists when the type is "slack"
	Mentions struct {
		OK       string `json:"ok,omitempty"`
		Warning  string `json:"warning,omitempty"`
		Critical string `json:"critical,omitempty"`
	} `json:"mentions,omitempty"`
	EnabledGraphImage bool `json:"enabledGraphImage,omitempty"`

	// Exists when the type is "slack" or "webhook"
	URL string `json:"url,omitempty"`

	// Exists when the type is "email", "slack", or "webhook"
	Events []string `json:"events,omitempty"`
}
<commit_msg>finish the definition of channel related API methods<commit_after>package mackerel

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Channel represents a Mackerel notification channel.
// ref. https://mackerel.io/api-docs/entry/channels
type Channel struct {
	ID string `json:"id"`
	ChannelWithoutID
}

// ChannelWithoutID represents a Mackerel notification channel without the ID.
type ChannelWithoutID struct {
	Name string `json:"name"`
	Type string `json:"type"`

	// Exists when the type is "email"
	Emails  []string `json:"emails,omitempty"`
	UserIDs []string `json:"userIds,omitempty"`

	// Exists when the type is "slack"
	Mentions struct {
		OK       string `json:"ok,omitempty"`
		Warning  string `json:"warning,omitempty"`
		Critical string `json:"critical,omitempty"`
	} `json:"mentions,omitempty"`
	EnabledGraphImage bool `json:"enabledGraphImage,omitempty"`

	// Exists when the type is "slack" or "webhook"
	URL string `json:"url,omitempty"`

	// Exists when the type is "email", "slack", or "webhook"
	Events []string `json:"events,omitempty"`
}

// ListChannels requests the channels API and returns a list of Channel
func (c *Client) ListChannels() ([]*Channel, error) {
	req, err := http.NewRequest("GET", c.urlFor("/api/v0/channels").String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		Channels []*Channel `json:"channels"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.Channels, err
}

// CreateChannel requests the channels API with the given params to create a channel and returns the created channel.
func (c *Client) CreateChannel(param *ChannelWithoutID) (*Channel, error) {
	resp, err := c.PostJSON("/api/v0/channels", param)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	channel := &Channel{}
	err = json.NewDecoder(resp.Body).Decode(channel)
	if err != nil {
		return nil, err
	}
	return channel, nil
}

// DeleteChannel requests the channels API with the given id to delete the specified channel, and returns the deleted channel.
func (c *Client) DeleteChannel(id string) (*Channel, error) {
	req, err := http.NewRequest(
		"DELETE",
		c.urlFor(fmt.Sprintf("/api/v0/channels/%s", id)).String(),
		nil,
	)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")

	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	channel := &Channel{}
	err = json.NewDecoder(resp.Body).Decode(channel)
	if err != nil {
		return nil, err
	}
	return channel, nil
}
<|endoftext|>


<commit_before>package inflator

import (
	"bufio"
	"cred-alert/mimetype"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
)

//go:generate counterfeiter . Inflator

type Inflator interface {
	Inflate(lager.Logger, string, string, string) error
}

type inflator struct {
	logfile *os.File
}

func New() *inflator {
	f, err := ioutil.TempFile("", "inflator-errors")
	if err != nil {
		panic("failed creating temp file: " + err.Error())
	}

	return &inflator{
		logfile: f,
	}
}

func (i *inflator) LogPath() string {
	return i.logfile.Name()
}

func (i *inflator) Close() error {
	return i.logfile.Close()
}

func (i *inflator) Inflate(logger lager.Logger, mime, archivePath, destination string) error {
	i.extractFile(mime, archivePath, destination)
	return i.recursivelyExtractArchivesInDir(logger, destination)
}

func (i *inflator) extractFile(mime, path, destination string) {
	err := os.MkdirAll(destination, 0755)
	if err != nil {
		panic(err.Error())
	}

	var cmd *exec.Cmd
	switch mime {
	case "application/zip":
		cmd = exec.Command("unzip", "-P", "", "-d", destination, path)
	case "application/x-tar":
		cmd = exec.Command("tar", "xf", path, "-C", destination)
	case "application/gzip", "application/x-gzip":
		fileName := filepath.Base(path)
		fileNameWithoutExt := fileName[:len(fileName)-len(filepath.Ext(fileName))]
		output, err := os.Create(filepath.Join(destination, fileNameWithoutExt))
		if err != nil {
			panic(err.Error())
		}
		defer output.Close()

		cmd = exec.Command("gunzip", "-c", path)
		cmd.Stdout = output
	default:
		panic(fmt.Sprintf("don't know how to extract %s", mime))
	}

	cmd.Stderr = i.logfile
	err = cmd.Run()
	if err != nil {
		// We've already logged the output to a file. Let's just keep going.
	}
}

func (i *inflator) recursivelyExtractArchivesInDir(logger lager.Logger, dir string) error {
	children, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for c := range children {
		basename := children[c].Name()
		absPath := filepath.Join(dir, basename)

		if children[c].IsDir() {
			err := i.recursivelyExtractArchivesInDir(logger, absPath)
			if err != nil {
				return err
			}
			continue
		}

		if !children[c].Mode().IsRegular() {
			continue
		}

		_, found := nonArchiveExtensions[filepath.Ext(basename)]
		if !found {
			fh, err := os.Open(absPath)
			if err != nil {
				return err
			}

			br := bufio.NewReader(fh)
			mime, isArchive := mimetype.IsArchive(logger, br)
			err = fh.Close()
			if err != nil {
				return err
			}

			if isArchive {
				extractDir := filepath.Join(dir, basename+"-contents")
				i.extractFile(mime, absPath, extractDir)

				err = os.RemoveAll(absPath)
				if err != nil {
					return err
				}

				err = i.recursivelyExtractArchivesInDir(logger, extractDir)
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

var nonArchiveExtensions = map[string]struct{}{
	".MF":           struct{}{},
	".S":            struct{}{},
	".a":            struct{}{},
	".am":           struct{}{},
	".article":      struct{}{},
	".au":           struct{}{},
	".autotest":     struct{}{},
	".bash":         struct{}{},
	".bat":          struct{}{},
	".builder":      struct{}{},
	".c":            struct{}{},
	".ca":           struct{}{},
	".cc":           struct{}{},
	".cert":         struct{}{},
	".cfg":          struct{}{},
	".class":        struct{}{},
	".classpath":    struct{}{},
	".cmake":        struct{}{},
	".cnf":          struct{}{},
	".column":       struct{}{},
	".conf":         struct{}{},
	".cpp":          struct{}{},
	".crt":          struct{}{},
	".css":          struct{}{},
	".csv":          struct{}{},
	".dat":          struct{}{},
	".data":         struct{}{},
	".decTest":      struct{}{},
	".def":          struct{}{},
	".devtools":     struct{}{},
	".dir":          struct{}{},
	".document":     struct{}{},
	".dtd":          struct{}{},
	".dumped":       struct{}{},
	".ec":           struct{}{},
	".ecpp":         struct{}{},
	".editorconfig": struct{}{},
	".ejava":        struct{}{},
	".ejs":          struct{}{},
	".eot":          struct{}{},
	".eperl":        struct{}{},
	".ephp":         struct{}{},
	".erb":          struct{}{},
	".erubis":       struct{}{},
	".eruby":        struct{}{},
	".escheme":      struct{}{},
	".example":      struct{}{},
	".exe":          struct{}{},
	".exp":          struct{}{},
	".fcgi":         struct{}{},
	".feature":      struct{}{},
	".gemfile":      struct{}{},
	".gemspec":      struct{}{},
	".gemtest":      struct{}{},
	".gif":          struct{}{},
	".gitignore":    struct{}{},
	".gitkeep":      struct{}{},
	".gitmodules":   struct{}{},
	".go":           struct{}{},
	".golden":       struct{}{},
	".gyp":          struct{}{},
	".h":            struct{}{},
	".haml":         struct{}{},
	".hoerc":        struct{}{},
	".hp":           struct{}{},
	".hpp":          struct{}{},
	".html":         struct{}{},
	".ico":          struct{}{},
	".iml":          struct{}{},
	".in":           struct{}{},
	".input":        struct{}{},
	".irbrc":        struct{}{},
	".iso":          struct{}{}, // to be removed when we support .iso
	".java":         struct{}{},
	".jpeg":         struct{}{},
	".jpg":          struct{}{},
	".jrubydir":     struct{}{},
	".js":           struct{}{},
	".json":         struct{}{},
	".jsp":          struct{}{},
	".keep":         struct{}{},
	".key":          struct{}{},
	".kpeg":         struct{}{},
	".liquid":       struct{}{},
	".list":         struct{}{},
	".lock":         struct{}{},
	".log":          struct{}{},
	".m4":           struct{}{},
	".mab":          struct{}{},
	".markdown":     struct{}{},
	".md":           struct{}{},
	".md5sums":      struct{}{},
	".mf":           struct{}{},
	".mk":           struct{}{},
	".mo":           struct{}{},
	".monitrc":      struct{}{},
	".msg":          struct{}{},
	".mspec":        struct{}{},
	".nokogiri":     struct{}{},
	".npmignore":    struct{}{},
	".obj":          struct{}{},
	".opts":         struct{}{},
	".out":          struct{}{},
	".ovf":          struct{}{},
	".patch":        struct{}{},
	".pdf":          struct{}{},
	".pem":          struct{}{},
	".php":          struct{}{},
	".phpt":         struct{}{},
	".pl":           struct{}{},
	".pm":           struct{}{},
	".png":          struct{}{},
	".po":           struct{}{},
	".postinst":     struct{}{},
	".postrm":       struct{}{},
	".project":      struct{}{},
	".properties":   struct{}{},
	".proto":        struct{}{},
	".psf":          struct{}{},
	".py":           struct{}{},
	".pyc":          struct{}{},
	".pyo":          struct{}{},
	".radius":       struct{}{},
	".rake":         struct{}{},
	".rake_example": struct{}{},
	".rb":           struct{}{},
	".rdoc":         struct{}{},
	".reek":         struct{}{},
	".reg":          struct{}{},
	".result":       struct{}{},
	".rhtml":        struct{}{},
	".rid":          struct{}{},
	".rl":           struct{}{},
	".rsc":          struct{}{},
	".rspec":        struct{}{},
	".rst":          struct{}{},
	".ru":           struct{}{},
	".ruby-gemset":  struct{}{},
	".ruby-version": struct{}{},
	".ry":           struct{}{},
	".s":            struct{}{},
	".sample":       struct{}{},
	".sass":         struct{}{},
	".sgml":         struct{}{},
	".sh":           struct{}{},
	".slim":         struct{}{},
	".sng":          struct{}{},
	".so":           struct{}{},
	".sql":          struct{}{},
	".src":          struct{}{},
	".str":          struct{}{},
	".supp":         struct{}{},
	".svg":          struct{}{},
	".t":            struct{}{},
	".test":         struct{}{},
	".text":         struct{}{},
	".thor":         struct{}{},
	".tmpl":         struct{}{},
	".tsv":          struct{}{},
	".tt":           struct{}{},
	".ttf":          struct{}{},
	".txt":          struct{}{},
	".utf8":         struct{}{},
	".vcproj":       struct{}{},
	".vmdk":         struct{}{},
	".x":            struct{}{},
	".xhtml":        struct{}{},
	".xml":          struct{}{},
	".xsd":          struct{}{},
	".xyz":          struct{}{},
	".y":            struct{}{},
	".yaml":         struct{}{},
	".yardopts":     struct{}{},
	".yml":          struct{}{},
}
<commit_msg>Don't hold onto bufio.Reader longer than necessary<commit_after>package inflator

import (
	"bufio"
	"cred-alert/mimetype"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
)

//go:generate counterfeiter . Inflator

type Inflator interface {
	Inflate(lager.Logger, string, string, string) error
}

type inflator struct {
	logfile *os.File
}

func New() *inflator {
	f, err := ioutil.TempFile("", "inflator-errors")
	if err != nil {
		panic("failed creating temp file: " + err.Error())
	}

	return &inflator{
		logfile: f,
	}
}

func (i *inflator) LogPath() string {
	return i.logfile.Name()
}

func (i *inflator) Close() error {
	return i.logfile.Close()
}

func (i *inflator) Inflate(logger lager.Logger, mime, archivePath, destination string) error {
	i.extractFile(mime, archivePath, destination)
	return i.recursivelyExtractArchivesInDir(logger, destination)
}

func (i *inflator) extractFile(mime, path, destination string) {
	err := os.MkdirAll(destination, 0755)
	if err != nil {
		panic(err.Error())
	}

	var cmd *exec.Cmd
	switch mime {
	case "application/zip":
		cmd = exec.Command("unzip", "-P", "", "-d", destination, path)
	case "application/x-tar":
		cmd = exec.Command("tar", "xf", path, "-C", destination)
	case "application/gzip", "application/x-gzip":
		fileName := filepath.Base(path)
		fileNameWithoutExt := fileName[:len(fileName)-len(filepath.Ext(fileName))]
		output, err := os.Create(filepath.Join(destination, fileNameWithoutExt))
		if err != nil {
			panic(err.Error())
		}
		defer output.Close()

		cmd = exec.Command("gunzip", "-c", path)
		cmd.Stdout = output
	default:
		panic(fmt.Sprintf("don't know how to extract %s", mime))
	}

	cmd.Stderr = i.logfile
	err = cmd.Run()
	if err != nil {
		// We've already logged the output to a file. Let's just keep going.
	}
}

func (i *inflator) recursivelyExtractArchivesInDir(logger lager.Logger, dir string) error {
	children, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for c := range children {
		basename := children[c].Name()
		absPath := filepath.Join(dir, basename)

		if children[c].IsDir() {
			err := i.recursivelyExtractArchivesInDir(logger, absPath)
			if err != nil {
				return err
			}
			continue
		}

		if !children[c].Mode().IsRegular() {
			continue
		}

		_, found := nonArchiveExtensions[filepath.Ext(basename)]
		if !found {
			fh, err := os.Open(absPath)
			if err != nil {
				return err
			}

			mime, isArchive := mimetype.IsArchive(logger, bufio.NewReader(fh))
			err = fh.Close()
			if err != nil {
				return err
			}

			if isArchive {
				extractDir := filepath.Join(dir, basename+"-contents")
				i.extractFile(mime, absPath, extractDir)

				err = os.RemoveAll(absPath)
				if err != nil {
					return err
				}

				err = i.recursivelyExtractArchivesInDir(logger, extractDir)
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

var nonArchiveExtensions = map[string]struct{}{
	".MF":           struct{}{},
	".S":            struct{}{},
	".a":            struct{}{},
	".am":           struct{}{},
	".article":      struct{}{},
	".au":           struct{}{},
	".autotest":     struct{}{},
	".bash":         struct{}{},
	".bat":          struct{}{},
	".builder":      struct{}{},
	".c":            struct{}{},
	".ca":           struct{}{},
	".cc":           struct{}{},
	".cert":         struct{}{},
	".cfg":          struct{}{},
	".class":        struct{}{},
	".classpath":    struct{}{},
	".cmake":        struct{}{},
	".cnf":          struct{}{},
	".column":       struct{}{},
	".conf":         struct{}{},
	".cpp":          struct{}{},
	".crt":          struct{}{},
	".css":          struct{}{},
	".csv":          struct{}{},
	".dat":          struct{}{},
	".data":         struct{}{},
	".decTest":      struct{}{},
	".def":          struct{}{},
	".devtools":     struct{}{},
	".dir":          struct{}{},
	".document":     struct{}{},
	".dtd":          struct{}{},
	".dumped":       struct{}{},
	".ec":           struct{}{},
	".ecpp":         struct{}{},
	".editorconfig": struct{}{},
	".ejava":        struct{}{},
	".ejs":          struct{}{},
	".eot":          struct{}{},
	".eperl":        struct{}{},
	".ephp":         struct{}{},
	".erb":          struct{}{},
	".erubis":       struct{}{},
	".eruby":        struct{}{},
	".escheme":      struct{}{},
	".example":      struct{}{},
	".exe":          struct{}{},
	".exp":          struct{}{},
	".fcgi":         struct{}{},
	".feature":      struct{}{},
	".gemfile":      struct{}{},
	".gemspec":      struct{}{},
	".gemtest":      struct{}{},
	".gif":          struct{}{},
	".gitignore":    struct{}{},
	".gitkeep":      struct{}{},
	".gitmodules":   struct{}{},
	".go":           struct{}{},
	".golden":       struct{}{},
	".gyp":          struct{}{},
	".h":            struct{}{},
	".haml":         struct{}{},
	".hoerc":        struct{}{},
	".hp":           struct{}{},
	".hpp":          struct{}{},
	".html":         struct{}{},
	".ico":          struct{}{},
	".iml":          struct{}{},
	".in":           struct{}{},
	".input":        struct{}{},
	".irbrc":        struct{}{},
	".iso":          struct{}{}, // to be removed when we support .iso
	".java":         struct{}{},
	".jpeg":         struct{}{},
	".jpg":          struct{}{},
	".jrubydir":     struct{}{},
	".js":           struct{}{},
	".json":         struct{}{},
	".jsp":          struct{}{},
	".keep":         struct{}{},
	".key":          struct{}{},
	".kpeg":         struct{}{},
	".liquid":       struct{}{},
	".list":         struct{}{},
	".lock":         struct{}{},
	".log":          struct{}{},
	".m4":           struct{}{},
	".mab":          struct{}{},
	".markdown":     struct{}{},
	".md":           struct{}{},
	".md5sums":      struct{}{},
	".mf":           struct{}{},
	".mk":           struct{}{},
	".mo":           struct{}{},
	".monitrc":      struct{}{},
	".msg":          struct{}{},
	".mspec":        struct{}{},
	".nokogiri":     struct{}{},
	".npmignore":    struct{}{},
	".obj":          struct{}{},
	".opts":         struct{}{},
	".out":          struct{}{},
	".ovf":          struct{}{},
	".patch":        struct{}{},
	".pdf":          struct{}{},
	".pem":          struct{}{},
	".php":          struct{}{},
	".phpt":         struct{}{},
	".pl":           struct{}{},
	".pm":           struct{}{},
	".png":          struct{}{},
	".po":           struct{}{},
	".postinst":     struct{}{},
	".postrm":       struct{}{},
	".project":      struct{}{},
	".properties":   struct{}{},
	".proto":        struct{}{},
	".psf":          struct{}{},
	".py":           struct{}{},
	".pyc":          struct{}{},
	".pyo":          struct{}{},
	".radius":       struct{}{},
	".rake":         struct{}{},
	".rake_example": struct{}{},
	".rb":           struct{}{},
	".rdoc":         struct{}{},
	".reek":         struct{}{},
	".reg":          struct{}{},
	".result":       struct{}{},
	".rhtml":        struct{}{},
	".rid":          struct{}{},
	".rl":           struct{}{},
	".rsc":          struct{}{},
	".rspec":        struct{}{},
	".rst":          struct{}{},
	".ru":           struct{}{},
	".ruby-gemset":  struct{}{},
	".ruby-version": struct{}{},
	".ry":           struct{}{},
	".s":            struct{}{},
	".sample":       struct{}{},
	".sass":         struct{}{},
	".sgml":         struct{}{},
	".sh":           struct{}{},
	".slim":         struct{}{},
	".sng":          struct{}{},
	".so":           struct{}{},
	".sql":          struct{}{},
	".src":          struct{}{},
	".str":          struct{}{},
	".supp":         struct{}{},
	".svg":          struct{}{},
	".t":            struct{}{},
	".test":         struct{}{},
	".text":         struct{}{},
	".thor":         struct{}{},
	".tmpl":         struct{}{},
	".tsv":          struct{}{},
	".tt":           struct{}{},
	".ttf":          struct{}{},
	".txt":          struct{}{},
	".utf8":         struct{}{},
	".vcproj":       struct{}{},
	".vmdk":         struct{}{},
	".x":            struct{}{},
	".xhtml":        struct{}{},
	".xml":          struct{}{},
	".xsd":          struct{}{},
	".xyz":          struct{}{},
	".y":            struct{}{},
	".yaml":         struct{}{},
	".yardopts":     struct{}{},
	".yml":          struct{}{},
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
)

// Dump dumps the AST defined by `node` as a list of sexps.
// Returns a string suitable for printing.
func (node *Node) Dump() string {
	str := ""
	str += node.Value

	if len(node.Flags) > 0 {
		str += fmt.Sprintf(" %q", node.Flags)
	}

	for _, n := range node.Children {
		str += "(" + n.Dump() + ")\n"
	}

	for n := node.Next; n != nil; n = n.Next {
		if len(n.Children) > 0 {
			str += " " + n.Dump()
		} else {
			str += " " + strconv.Quote(n.Value)
		}
	}

	return strings.TrimSpace(str)
}

// performs the dispatch based on the two primal strings, cmd and args. Please
// look at the dispatch table in parser.go to see how these dispatchers work.
func fullDispatch(cmd, args string, d *Directive) (*Node, map[string]bool, error) {
	fn := dispatch[cmd]

	// Ignore invalid Dockerfile instructions
	if fn == nil {
		fn = parseIgnore
	}

	sexp, attrs, err := fn(args, d)
	if err != nil {
		return nil, nil, err
	}

	return sexp, attrs, nil
}

// splitCommand takes a single line of text and parses out the cmd and args,
// which are used for dispatching to more exact parsing functions.
func splitCommand(line string) (string, []string, string, error) {
	var args string
	var flags []string

	// Make sure we get the same results irrespective of leading/trailing spaces
	cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2)
	cmd := strings.ToLower(cmdline[0])

	if len(cmdline) == 2 {
		var err error
		args, flags, err = extractBuilderFlags(cmdline[1])
		if err != nil {
			return "", nil, "", err
		}
	}

	return cmd, flags, strings.TrimSpace(args), nil
}

// covers comments and empty lines. Lines should be trimmed before passing to
// this function.
func stripComments(line string) string {
	// string is already trimmed at this point
	if tokenComment.MatchString(line) {
		return tokenComment.ReplaceAllString(line, "")
	}

	return line
}

func extractBuilderFlags(line string) (string, []string, error) {
	// Parses the BuilderFlags and returns the remaining part of the line

	const (
		inSpaces = iota // looking for start of a word
		inWord
		inQuote
	)

	words := []string{}
	phase := inSpaces
	word := ""
	quote := '\000'
	blankOK := false
	var ch rune

	for pos := 0; pos <= len(line); pos++ {
		if pos != len(line) {
			ch = rune(line[pos])
		}

		if phase == inSpaces { // Looking for start of word
			if pos == len(line) { // end of input
				break
			}
			if unicode.IsSpace(ch) { // skip spaces
				continue
			}

			// Only keep going if the next word starts with --
			if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' {
				return line[pos:], words, nil
			}

			phase = inWord // found someting with "--", fall through
		}
		if (phase == inWord || phase == inQuote) && (pos == len(line)) {
			if word != "--" && (blankOK || len(word) > 0) {
				words = append(words, word)
			}
			break
		}
		if phase == inWord {
			if unicode.IsSpace(ch) {
				phase = inSpaces
				if word == "--" {
					return line[pos:], words, nil
				}
				if blankOK || len(word) > 0 {
					words = append(words, word)
				}
				word = ""
				blankOK = false
				continue
			}
			if ch == '\'' || ch == '"' {
				quote = ch
				blankOK = true
				phase = inQuote
				continue
			}
			if ch == '\\' {
				if pos+1 == len(line) {
					continue // just skip \ at end
				}
				pos++
				ch = rune(line[pos])
			}
			word += string(ch)
			continue
		}
		if phase == inQuote {
			if ch == quote {
				phase = inWord
				continue
			}
			if ch == '\\' {
				if pos+1 == len(line) {
					phase = inWord
					continue // just skip \ at end
				}
				pos++
				ch = rune(line[pos])
			}
			word += string(ch)
		}
	}

	return "", words, nil
}
<commit_msg>fix misspell in utils.go<commit_after>package parser

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
)

// Dump dumps the AST defined by `node` as a list of sexps.
// Returns a string suitable for printing.
func (node *Node) Dump() string {
	str := ""
	str += node.Value

	if len(node.Flags) > 0 {
		str += fmt.Sprintf(" %q", node.Flags)
	}

	for _, n := range node.Children {
		str += "(" + n.Dump() + ")\n"
	}

	for n := node.Next; n != nil; n = n.Next {
		if len(n.Children) > 0 {
			str += " " + n.Dump()
		} else {
			str += " " + strconv.Quote(n.Value)
		}
	}

	return strings.TrimSpace(str)
}

// performs the dispatch based on the two primal strings, cmd and args. Please
// look at the dispatch table in parser.go to see how these dispatchers work.
func fullDispatch(cmd, args string, d *Directive) (*Node, map[string]bool, error) {
	fn := dispatch[cmd]

	// Ignore invalid Dockerfile instructions
	if fn == nil {
		fn = parseIgnore
	}

	sexp, attrs, err := fn(args, d)
	if err != nil {
		return nil, nil, err
	}

	return sexp, attrs, nil
}

// splitCommand takes a single line of text and parses out the cmd and args,
// which are used for dispatching to more exact parsing functions.
func splitCommand(line string) (string, []string, string, error) {
	var args string
	var flags []string

	// Make sure we get the same results irrespective of leading/trailing spaces
	cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2)
	cmd := strings.ToLower(cmdline[0])

	if len(cmdline) == 2 {
		var err error
		args, flags, err = extractBuilderFlags(cmdline[1])
		if err != nil {
			return "", nil, "", err
		}
	}

	return cmd, flags, strings.TrimSpace(args), nil
}

// covers comments and empty lines. Lines should be trimmed before passing to
// this function.
func stripComments(line string) string {
	// string is already trimmed at this point
	if tokenComment.MatchString(line) {
		return tokenComment.ReplaceAllString(line, "")
	}

	return line
}

func extractBuilderFlags(line string) (string, []string, error) {
	// Parses the BuilderFlags and returns the remaining part of the line

	const (
		inSpaces = iota // looking for start of a word
		inWord
		inQuote
	)

	words := []string{}
	phase := inSpaces
	word := ""
	quote := '\000'
	blankOK := false
	var ch rune

	for pos := 0; pos <= len(line); pos++ {
		if pos != len(line) {
			ch = rune(line[pos])
		}

		if phase == inSpaces { // Looking for start of word
			if pos == len(line) { // end of input
				break
			}
			if unicode.IsSpace(ch) { // skip spaces
				continue
			}

			// Only keep going if the next word starts with --
			if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' {
				return line[pos:], words, nil
			}

			phase = inWord // found something with "--", fall through
		}
		if (phase == inWord || phase == inQuote) && (pos == len(line)) {
			if word != "--" && (blankOK || len(word) > 0) {
				words = append(words, word)
			}
			break
		}
		if phase == inWord {
			if unicode.IsSpace(ch) {
				phase = inSpaces
				if word == "--" {
					return line[pos:], words, nil
				}
				if blankOK || len(word) > 0 {
					words = append(words, word)
				}
				word = ""
				blankOK = false
				continue
			}
			if ch == '\'' || ch == '"' {
				quote = ch
				blankOK = true
				phase = inQuote
				continue
			}
			if ch == '\\' {
				if pos+1 == len(line) {
					continue // just skip \ at end
				}
				pos++
				ch = rune(line[pos])
			}
			word += string(ch)
			continue
		}
		if phase == inQuote {
			if ch == quote {
				phase = inWord
				continue
			}
			if ch == '\\' {
				if pos+1 == len(line) {
					phase = inWord
					continue // just skip \ at end
				}
				pos++
				ch = rune(line[pos])
			}
			word += string(ch)
		}
	}

	return "", words, nil
}
<|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 exec

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

func helperCommand(s ...string) *Cmd {
	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := Command(os.Args[0], cs...)
	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
	return cmd
}

func TestEcho(t *testing.T) {
	bs, err := helperCommand("echo", "foo bar", "baz").Output()
	if err != nil {
		t.Errorf("echo: %v", err)
	}
	if g, e := string(bs), "foo bar baz\n"; g != e {
		t.Errorf("echo: want %q, got %q", e, g)
	}
}

func TestCatStdin(t *testing.T) {
	// Cat, testing stdin and stdout.
	input := "Input string\nLine 2"
	p := helperCommand("cat")
	p.Stdin = strings.NewReader(input)
	bs, err := p.Output()
	if err != nil {
		t.Errorf("cat: %v", err)
	}
	s := string(bs)
	if s != input {
		t.Errorf("cat: want %q, got %q", input, s)
	}
}

func TestCatGoodAndBadFile(t *testing.T) {
	// Testing combined output and error values.
	bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
	if _, ok := err.(*ExitError); !ok {
		t.Errorf("expected *ExitError from cat combined; got %T: %v", err, err)
	}
	s := string(bs)
	sp := strings.SplitN(s, "\n", 2)
	if len(sp) != 2 {
		t.Fatalf("expected two lines from cat; got %q", s)
	}
	errLine, body := sp[0], sp[1]
	if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
		t.Errorf("expected stderr to complain about file; got %q", errLine)
	}
	if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") {
		t.Errorf("expected test code; got %q (len %d)", body, len(body))
	}
}

func TestNoExistBinary(t *testing.T) {
	// Can't run a non-existent binary
	err := Command("/no-exist-binary").Run()
	if err == nil {
		t.Error("expected error from /no-exist-binary")
	}
}

func TestExitStatus(t *testing.T) {
	// Test that exit values are returned correctly
	err := helperCommand("exit", "42").Run()
	if werr, ok := err.(*ExitError); ok {
		if s, e := werr.Error(), "exit status 42"; s != e {
			t.Errorf("from exit 42 got exit %q, want %q", s, e)
		}
	} else {
		t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err)
	}
}

func TestPipes(t *testing.T) {
	check := func(what string, err error) {
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
	}
	// Cat, testing stdin and stdout.
	c := helperCommand("pipetest")
	stdin, err := c.StdinPipe()
	check("StdinPipe", err)
	stdout, err := c.StdoutPipe()
	check("StdoutPipe", err)
	stderr, err := c.StderrPipe()
	check("StderrPipe", err)

	outbr := bufio.NewReader(stdout)
	errbr := bufio.NewReader(stderr)
	line := func(what string, br *bufio.Reader) string {
		line, _, err := br.ReadLine()
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
		return string(line)
	}

	err = c.Start()
	check("Start", err)

	_, err = stdin.Write([]byte("O:I am output\n"))
	check("first stdin Write", err)
	if g, e := line("first output line", outbr), "O:I am output"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("E:I am error\n"))
	check("second stdin Write", err)
	if g, e := line("first error line", errbr), "E:I am error"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("O:I am output2\n"))
	check("third stdin Write 3", err)
	if g, e := line("second output line", outbr), "O:I am output2"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	stdin.Close()
	err = c.Wait()
	check("Wait", err)
}

func TestExtraFiles(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Logf("no operating system support; skipping")
		return
	}

	// Force network usage, to verify the epoll (or whatever) fd
	// doesn't leak to the child,
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	// Force TLS root certs to be loaded (which might involve
	// cgo), to make sure none of that potential C code leaks fds.
	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello"))
	}))
	defer ts.Close()
	http.Get(ts.URL) // ignore result; just calling to force root cert loading

	tf, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("TempFile: %v", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	const text = "Hello, fd 3!"
	_, err = tf.Write([]byte(text))
	if err != nil {
		t.Fatalf("Write: %v", err)
	}
	_, err = tf.Seek(0, os.SEEK_SET)
	if err != nil {
		t.Fatalf("Seek: %v", err)
	}

	c := helperCommand("read3")
	c.ExtraFiles = []*os.File{tf}
	bs, err := c.CombinedOutput()
	if err != nil {
		t.Fatalf("CombinedOutput: %v; output %q", err, bs)
	}
	if string(bs) != text {
		t.Errorf("got %q; want %q", string(bs), text)
	}
}

// TestHelperProcess isn't a real test. It's used as a helper process
// for TestParameterRun.
func TestHelperProcess(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	defer os.Exit(0)

	args := os.Args
	for len(args) > 0 {
		if args[0] == "--" {
			args = args[1:]
			break
		}
		args = args[1:]
	}
	if len(args) == 0 {
		fmt.Fprintf(os.Stderr, "No command\n")
		os.Exit(2)
	}

	cmd, args := args[0], args[1:]
	switch cmd {
	case "echo":
		iargs := []interface{}{}
		for _, s := range args {
			iargs = append(iargs, s)
		}
		fmt.Println(iargs...)
	case "cat":
		if len(args) == 0 {
			io.Copy(os.Stdout, os.Stdin)
			return
		}
		exit := 0
		for _, fn := range args {
			f, err := os.Open(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				exit = 2
			} else {
				defer f.Close()
				io.Copy(os.Stdout, f)
			}
		}
		os.Exit(exit)
	case "pipetest":
		bufr := bufio.NewReader(os.Stdin)
		for {
			line, _, err := bufr.ReadLine()
			if err == io.EOF {
				break
			} else if err != nil {
				os.Exit(1)
			}
			if bytes.HasPrefix(line, []byte("O:")) {
				os.Stdout.Write(line)
				os.Stdout.Write([]byte{'\n'})
			} else if bytes.HasPrefix(line, []byte("E:")) {
				os.Stderr.Write(line)
				os.Stderr.Write([]byte{'\n'})
			} else {
				os.Exit(1)
			}
		}
	case "read3": // read fd 3
		fd3 := os.NewFile(3, "fd3")
		bs, err := ioutil.ReadAll(fd3)
		if err != nil {
			fmt.Printf("ReadAll from fd 3: %v", err)
			os.Exit(1)
		}
		switch runtime.GOOS {
		case "darwin":
			// TODO(bradfitz): broken? Sometimes.
			// http://golang.org/issue/2603
			// Skip this additional part of the test for now.
		default:
			// Now verify that there are no other open fds.
			var files []*os.File
			for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {
				f, err := os.Open(os.Args[0])
				if err != nil {
					fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
					os.Exit(1)
				}
				if got := f.Fd(); got != wantfd {
					fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
					out, _ := Command("lsof", "-p", fmt.Sprint(os.Getpid())).CombinedOutput()
					fmt.Print(string(out))
					os.Exit(1)
				}
				files = append(files, f)
			}
			for _, f := range files {
				f.Close()
			}
		}
		os.Stderr.Write(bs)
	case "exit":
		n, _ := strconv.Atoi(args[0])
		os.Exit(n)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
		os.Exit(2)
	}
}
<commit_msg>os/exec: TestExtraFiles - close any leaked file descriptors<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 exec

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"testing"
)

func helperCommand(s ...string) *Cmd {
	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := Command(os.Args[0], cs...)
	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
	return cmd
}

func TestEcho(t *testing.T) {
	bs, err := helperCommand("echo", "foo bar", "baz").Output()
	if err != nil {
		t.Errorf("echo: %v", err)
	}
	if g, e := string(bs), "foo bar baz\n"; g != e {
		t.Errorf("echo: want %q, got %q", e, g)
	}
}

func TestCatStdin(t *testing.T) {
	// Cat, testing stdin and stdout.
	input := "Input string\nLine 2"
	p := helperCommand("cat")
	p.Stdin = strings.NewReader(input)
	bs, err := p.Output()
	if err != nil {
		t.Errorf("cat: %v", err)
	}
	s := string(bs)
	if s != input {
		t.Errorf("cat: want %q, got %q", input, s)
	}
}

func TestCatGoodAndBadFile(t *testing.T) {
	// Testing combined output and error values.
	bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
	if _, ok := err.(*ExitError); !ok {
		t.Errorf("expected *ExitError from cat combined; got %T: %v", err, err)
	}
	s := string(bs)
	sp := strings.SplitN(s, "\n", 2)
	if len(sp) != 2 {
		t.Fatalf("expected two lines from cat; got %q", s)
	}
	errLine, body := sp[0], sp[1]
	if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
		t.Errorf("expected stderr to complain about file; got %q", errLine)
	}
	if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") {
		t.Errorf("expected test code; got %q (len %d)", body, len(body))
	}
}

func TestNoExistBinary(t *testing.T) {
	// Can't run a non-existent binary
	err := Command("/no-exist-binary").Run()
	if err == nil {
		t.Error("expected error from /no-exist-binary")
	}
}

func TestExitStatus(t *testing.T) {
	// Test that exit values are returned correctly
	err := helperCommand("exit", "42").Run()
	if werr, ok := err.(*ExitError); ok {
		if s, e := werr.Error(), "exit status 42"; s != e {
			t.Errorf("from exit 42 got exit %q, want %q", s, e)
		}
	} else {
		t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err)
	}
}

func TestPipes(t *testing.T) {
	check := func(what string, err error) {
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
	}
	// Cat, testing stdin and stdout.
	c := helperCommand("pipetest")
	stdin, err := c.StdinPipe()
	check("StdinPipe", err)
	stdout, err := c.StdoutPipe()
	check("StdoutPipe", err)
	stderr, err := c.StderrPipe()
	check("StderrPipe", err)

	outbr := bufio.NewReader(stdout)
	errbr := bufio.NewReader(stderr)
	line := func(what string, br *bufio.Reader) string {
		line, _, err := br.ReadLine()
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
		return string(line)
	}

	err = c.Start()
	check("Start", err)

	_, err = stdin.Write([]byte("O:I am output\n"))
	check("first stdin Write", err)
	if g, e := line("first output line", outbr), "O:I am output"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("E:I am error\n"))
	check("second stdin Write", err)
	if g, e := line("first error line", errbr), "E:I am error"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("O:I am output2\n"))
	check("third stdin Write 3", err)
	if g, e := line("second output line", outbr), "O:I am output2"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	stdin.Close()
	err = c.Wait()
	check("Wait", err)
}

func TestExtraFiles(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Logf("no operating system support; skipping")
		return
	}

	// Ensure that file descriptors have not already been leaked into
	// our environment.
	for fd := os.Stderr.Fd() + 1; fd <= 101; fd++ {
		err := syscall.Close(fd)
		if err == nil {
			t.Logf("Something already leaked - closed fd %d", fd)
		}
	}

	// Force network usage, to verify the epoll (or whatever) fd
	// doesn't leak to the child,
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	// Force TLS root certs to be loaded (which might involve
	// cgo), to make sure none of that potential C code leaks fds.
	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello"))
	}))
	defer ts.Close()
	http.Get(ts.URL) // ignore result; just calling to force root cert loading

	tf, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("TempFile: %v", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	const text = "Hello, fd 3!"
	_, err = tf.Write([]byte(text))
	if err != nil {
		t.Fatalf("Write: %v", err)
	}
	_, err = tf.Seek(0, os.SEEK_SET)
	if err != nil {
		t.Fatalf("Seek: %v", err)
	}

	c := helperCommand("read3")
	c.ExtraFiles = []*os.File{tf}
	bs, err := c.CombinedOutput()
	if err != nil {
		t.Fatalf("CombinedOutput: %v; output %q", err, bs)
	}
	if string(bs) != text {
		t.Errorf("got %q; want %q", string(bs), text)
	}
}

// TestHelperProcess isn't a real test. It's used as a helper process
// for TestParameterRun.
func TestHelperProcess(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	defer os.Exit(0)

	// Determine which command to use to display open files.
	ofcmd := "lsof"
	switch runtime.GOOS {
	case "freebsd", "netbsd", "openbsd":
		ofcmd = "fstat"
	}

	args := os.Args
	for len(args) > 0 {
		if args[0] == "--" {
			args = args[1:]
			break
		}
		args = args[1:]
	}
	if len(args) == 0 {
		fmt.Fprintf(os.Stderr, "No command\n")
		os.Exit(2)
	}

	cmd, args := args[0], args[1:]
	switch cmd {
	case "echo":
		iargs := []interface{}{}
		for _, s := range args {
			iargs = append(iargs, s)
		}
		fmt.Println(iargs...)
	case "cat":
		if len(args) == 0 {
			io.Copy(os.Stdout, os.Stdin)
			return
		}
		exit := 0
		for _, fn := range args {
			f, err := os.Open(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				exit = 2
			} else {
				defer f.Close()
				io.Copy(os.Stdout, f)
			}
		}
		os.Exit(exit)
	case "pipetest":
		bufr := bufio.NewReader(os.Stdin)
		for {
			line, _, err := bufr.ReadLine()
			if err == io.EOF {
				break
			} else if err != nil {
				os.Exit(1)
			}
			if bytes.HasPrefix(line, []byte("O:")) {
				os.Stdout.Write(line)
				os.Stdout.Write([]byte{'\n'})
			} else if bytes.HasPrefix(line, []byte("E:")) {
				os.Stderr.Write(line)
				os.Stderr.Write([]byte{'\n'})
			} else {
				os.Exit(1)
			}
		}
	case "read3": // read fd 3
		fd3 := os.NewFile(3, "fd3")
		bs, err := ioutil.ReadAll(fd3)
		if err != nil {
			fmt.Printf("ReadAll from fd 3: %v", err)
			os.Exit(1)
		}
		switch runtime.GOOS {
		case "darwin":
			// TODO(bradfitz): broken? Sometimes.
			// http://golang.org/issue/2603
			// Skip this additional part of the test for now.
		default:
			// Now verify that there are no other open fds.
			var files []*os.File
			for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {
				f, err := os.Open(os.Args[0])
				if err != nil {
					fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
					os.Exit(1)
				}
				if got := f.Fd(); got != wantfd {
					fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
					out, _ := Command(ofcmd, "-p", fmt.Sprint(os.Getpid())).CombinedOutput()
					fmt.Print(string(out))
					os.Exit(1)
				}
				files = append(files, f)
			}
			for _, f := range files {
				f.Close()
			}
		}
		os.Stderr.Write(bs)
	case "exit":
		n, _ := strconv.Atoi(args[0])
		os.Exit(n)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package eval

// Misc builtin functions.

import (
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/elves/elvish/pkg/eval/vals"
	"github.com/elves/elvish/pkg/eval/vars"
	"github.com/elves/elvish/pkg/parse"
)

// Builtins that have not been put into their own groups go here.

func init() {
	addBuiltinFns(map[string]interface{}{
		"nop":        nop,
		"kind-of":    kindOf,
		"constantly": constantly,

		"resolve": resolve,

		"eval":    eval,
		"use-mod": useMod,
		"-source": source,

		// Time
		"esleep": sleep,
		"sleep":  sleep,
		"time":   timeCmd,

		"-ifaddrs": _ifaddrs,
	})

	// For rand and randint.
	rand.Seed(time.Now().UTC().UnixNano())
}

//elvdoc:fn nop
//
// ```elvish
// nop &any-opt= $value...
// ```
//
// Accepts arbitrary arguments and options and does exactly nothing.
//
// Examples:
//
// ```elvish-transcript
// ~> nop
// ~> nop a b c
// ~> nop &k=v
// ```
//
// Etymology: Various languages, in particular NOP in
// [assembly languages](https://en.wikipedia.org/wiki/NOP).

func nop(opts RawOptions, args ...interface{}) {
	// Do nothing
}

//elvdoc:fn kind-of
//
// ```elvish
// kind-of $value...
// ```
//
// Output the kinds of `$value`s. Example:
//
// ```elvish-transcript
// ~> kind-of lorem [] [&]
// ▶ string
// ▶ list
// ▶ map
// ```
//
// The terminology and definition of "kind" is subject to change.

func kindOf(fm *Frame, args ...interface{}) {
	out := fm.OutputChan()
	for _, a := range args {
		out <- vals.Kind(a)
	}
}

//elvdoc:fn constantly
//
// ```elvish
// constantly $value...
// ```
//
// Output a function that takes no arguments and outputs `$value`s when called.
// Examples:
//
// ```elvish-transcript
// ~> f=(constantly lorem ipsum)
// ~> $f
// ▶ lorem
// ▶ ipsum
// ```
//
// The above example is actually equivalent to simply `f = []{ put lorem ipsum }`;
// it is most useful when the argument is **not** a literal value, e.g.
//
// ```elvish-transcript
// ~> f = (constantly (uname))
// ~> $f
// ▶ Darwin
// ~> $f
// ▶ Darwin
// ```
//
// The above code only calls `uname` once, while if you do `f = []{ put (uname) }`,
// every time you invoke `$f`, `uname` will be called.
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/constantly).

func constantly(args ...interface{}) Callable {
	// TODO(xiaq): Repr of this function is not right.
	return NewGoFn(
		"created by constantly",
		func(fm *Frame) {
			out := fm.OutputChan()
			for _, v := range args {
				out <- v
			}
		},
	)
}

//elvdoc:fn resolve
//
// ```elvish
// resolve $command
// ```
//
// Output what `$command` resolves to in symbolic form. Command resolution is
// described in the [language reference](language.html#ordinary-command).
//
// Example:
//
// ```elvish-transcript
// ~> resolve echo
// ▶ <builtin echo>
// ~> fn f { }
// ~> resolve f
// ▶ <closure 0xc4201c24d0>
// ~> resolve cat
// ▶ <external cat>
// ```

func resolve(fm *Frame, head string) string {
	special, fnRef := resolveCmdHeadInternally(fm, head, nil)
	switch {
	case special != nil:
		return "special"
	case fnRef != nil:
		return "$" + head + FnSuffix
	default:
		return "(external " + parse.Quote(head) + ")"
	}
}

//elvdoc:fn eval
//
// ```elvish
// eval $code &ns=$nil
// ```
//
// Evaluates `$code`, which should be a string. The evaluation happens in the
// namespace specified by the `&ns` option. If it is `$nil` (the default), a
// fresh empty namespace is created.
//
// If `$code` fails to parse or compile, the parse error or compilation error is
// raised as an exception.
//
// Examples:
//
// ```elvish-transcript
// ~> eval 'put x'
// ▶ x
// ~> ns = (ns [&x=initial])
// ~> eval 'put $x; x = altered; put $x' &ns=$ns
// ▶ initial
// ▶ altered
// ~> put $ns[x]
// ▶ altered
// ```
//
// NOTE: Unlike the `eval` found in many other dynamic languages, `eval` cannot
// affect the current namespace:
//
// ```elvish-transcript
// ~> eval 'x = value'
// ~> put $x
// compilation error: variable $x not found
// [tty 4], line 1: put $x
// ```

type evalOpts struct{ Ns *Ns }

func (*evalOpts) SetDefaultOptions() {}

func eval(fm *Frame, opts evalOpts, code string) error {
	src := parse.Source{Name: fmt.Sprintf("[eval %d]", nextEvalCount()), Code: code}
	ns := opts.Ns
	if ns == nil {
		ns = new(Ns)
	}
	return evalInner(fm, src, ns, fm.traceback)
}

// Used to generate unique names for each source passed to eval.
var (
	evalCount      int
	evalCountMutex sync.Mutex
)

func nextEvalCount() int {
	evalCountMutex.Lock()
	defer evalCountMutex.Unlock()
	evalCount++
	return evalCount
}

//elvdoc:fn use-mod
//
// ```elvish
// use-mod $use-spec
// ```
//
// Imports a module, and outputs the namespace for the module.
//
// Most code should use the [use](language.html#importing-modules-with-use)
// special command instead.
//
// Examples:
//
// ```elvish-transcript
// ~> echo 'x = value' > a.elv
// ~> put (use-mod ./a)[x]
// ▶ value
// ```

func useMod(fm *Frame, spec string) (*Ns, error) {
	return use(fm, spec, fm.traceback)
}

//elvdoc:fn -source
//
// ```elvish
// -source $filename
// ```
//
// Read the named file, and evaluate it in a temporary namespace built from the
// current local and up scope.
//
// Examples:
//
// ```elvish-transcript
// ~> cat x.elv
// echo 'executing x.elv'
// foo = bar
// ~> -source x.elv
// executing x.elv
// ~> echo $foo
// bar
// ```
//
// Since the file is evaluated in a temporary namespace, any modifications to
// the namespace itself - creation of variables and deletion of variables - do
// not affect the code calling `-source`. For example:
//
// ```elvish-transcript
// ~> echo 'foo = lorem' > a.elv
// ~> -source a.elv
// ~> put $foo
// compilation error: 4-8 in [tty]: variable $foo not found
// compilation error: variable $foo not found
// [tty 3], line 1: put $foo
// ```
//
// However, the file may mutate variables that already exist, and such mutations
// are persisted:
//
// ```elvish
// ~> foo = lorem
// ~> echo 'foo = ipsum' > a.elv
// ~> -source a.elv
// ~> put $foo
// ▶ ipsum
// ```

func source(fm *Frame, fname string) error {
	code, err := readFileUTF8(fname)
	if err != nil {
		return err
	}
	src := parse.Source{Name: fname, Code: code, IsFile: true}
	tree, err := parse.ParseWithDeprecation(src, fm.ErrorFile())
	if err != nil {
		return err
	}
	// Amalgamate the up and local scope into a new scope to use as the global
	// scope to evaluate the code in.
	g := amalgamateNs(fm.local, fm.up)
	op, err := compile(fm.Builtin.static(), g.static(), tree, fm.ErrorFile())
	if err != nil {
		return err
	}
	newFm := fm.fork("[-source]")
	newFm.local = g
	newFm.srcMeta = src
	return op.Exec(newFm)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

func amalgamateNs(local, up *Ns) *Ns {
	slots := append([]vars.Var(nil), local.slots...)
	names := append([]string(nil), local.names...)
	for i := range up.slots {
		if local.lookup(up.names[i]) == -1 {
			slots = append(slots, up.slots[i])
			names = append(names, up.names[i])
		}
	}
	return &Ns{slots, names}
}

// TimeAfter is used by the sleep command to obtain a channel that is delivered
// a value after the specified time.
//
// It is a variable to allow for unit tests to efficiently test the behavior of
// the `sleep` command, both by eliminating an actual sleep and verifying the
// duration was properly parsed.
var TimeAfter = func(fm *Frame, d time.Duration) <-chan time.Time {
	return time.After(d)
}

//elvdoc:fn sleep
//
// ```elvish
// sleep $duration
// ```
//
// Pauses for at least the specified duration. The actual pause duration depends
// on the system.
//
// This only affects the current Elvish context. It does not affect any other
// contexts that might be executing in parallel as a consequence of a command
// such as [`peach`](#peach).
//
// A duration can be a simple [number](../language.html#number) (with optional
// fractional value) without an explicit unit suffix, with an implicit unit of
// seconds.
//
// A duration can also be a string written as a sequence of decimal numbers,
// each with optional fraction, plus a unit suffix. For example, "300ms",
// "1.5h" or "1h45m7s". Valid time units are "ns", "us" (or "µs"), "ms", "s",
// "m", "h".
//
// Passing a negative duration causes an exception; this is different from the
// typical BSD or GNU `sleep` command that silently exits with a success status
// without pausing when given a negative duration.
//
// See the [Go documentation](https://golang.org/pkg/time/#ParseDuration) for
// more information about how durations are parsed.
//
// Examples:
//
// ```elvish-transcript
// ~> sleep 0.1    # sleeps 0.1 seconds
// ~> sleep 100ms  # sleeps 0.1 seconds
// ~> sleep 1.5m   # sleeps 1.5 minutes
// ~> sleep 1m30s  # sleeps 1.5 minutes
// ~> sleep -1
// Exception: sleep duration must be >= zero
// [tty 8], line 1: sleep -1
// ```

func sleep(fm *Frame, duration interface{}) error {
	var d time.Duration

	switch duration := duration.(type) {
	case float64:
		d = time.Duration(float64(time.Second) * duration)
	case string:
		f, err := strconv.ParseFloat(duration, 64)
		if err == nil { // it's a simple number assumed to have units == seconds
			d = time.Duration(float64(time.Second) * f)
		} else {
			d, err = time.ParseDuration(duration)
			if err != nil {
				return errors.New("invalid sleep duration")
			}
		}
	default:
		return errors.New("invalid sleep duration")
	}

	if d < 0 {
		return fmt.Errorf("sleep duration must be >= zero")
	}

	select {
	case <-fm.Interrupts():
		return ErrInterrupted
	case <-TimeAfter(fm, d):
		return nil
	}
}

//elvdoc:fn time
//
// ```elvish
// time &on-end=$nil $callable
// ```
//
// Runs the callable, and call `$on-end` with the duration it took, as a
// number in seconds. If `$on-end` is `$nil` (the default), prints the
// duration in human-readable form.
//
// If `$callable` throws an exception, the exception is propagated after the
// on-end or default printing is done.
//
// If `$on-end` throws an exception, it is propagated, unless `$callable` has
// already thrown an exception.
//
// Example:
//
// ```elvish-transcript
// ~> time { sleep 1 }
// 1.006060647s
// ~> time { sleep 0.01 }
// 1.288977ms
// ~> t = ''
// ~> time &on-end=[x]{ t = $x } { sleep 1 }
// ~> put $t
// ▶ (float64 1.000925004)
// ~> time &on-end=[x]{ t = $x } { sleep 0.01 }
// ~> put $t
// ▶ (float64 0.011030208)
// ```

type timeOpt struct{ OnEnd Callable }

func (o *timeOpt) SetDefaultOptions() {}

func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
	t0 := time.Now()
	err := f.Call(fm, NoArgs, NoOpts)
	t1 := time.Now()

	dt := t1.Sub(t0)
	if opts.OnEnd != nil {
		newFm := fm.fork("on-end callback of time")
		errCb := opts.OnEnd.Call(newFm, []interface{}{dt.Seconds()}, NoOpts)
		if err == nil {
			err = errCb
		}
	} else {
		fmt.Fprintln(fm.OutputFile(), dt)
	}

	return err
}

//elvdoc:fn -ifaddrs
//
// ```elvish
// -ifaddrs
// ```
//
// Output all IP addresses of the current host.
//
// This should be part of a networking module instead of the builtin module.

func _ifaddrs(fm *Frame) error {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return err
	}
	out := fm.OutputChan()
	for _, addr := range addrs {
		out <- addr.String()
	}
	return nil
}
<commit_msg>Fix format of elvdoc for -source.<commit_after>package eval

// Misc builtin functions.

import (
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/elves/elvish/pkg/eval/vals"
	"github.com/elves/elvish/pkg/eval/vars"
	"github.com/elves/elvish/pkg/parse"
)

// Builtins that have not been put into their own groups go here.

func init() {
	addBuiltinFns(map[string]interface{}{
		"nop":        nop,
		"kind-of":    kindOf,
		"constantly": constantly,

		"resolve": resolve,

		"eval":    eval,
		"use-mod": useMod,
		"-source": source,

		// Time
		"esleep": sleep,
		"sleep":  sleep,
		"time":   timeCmd,

		"-ifaddrs": _ifaddrs,
	})

	// For rand and randint.
	rand.Seed(time.Now().UTC().UnixNano())
}

//elvdoc:fn nop
//
// ```elvish
// nop &any-opt= $value...
// ```
//
// Accepts arbitrary arguments and options and does exactly nothing.
//
// Examples:
//
// ```elvish-transcript
// ~> nop
// ~> nop a b c
// ~> nop &k=v
// ```
//
// Etymology: Various languages, in particular NOP in
// [assembly languages](https://en.wikipedia.org/wiki/NOP).

func nop(opts RawOptions, args ...interface{}) {
	// Do nothing
}

//elvdoc:fn kind-of
//
// ```elvish
// kind-of $value...
// ```
//
// Output the kinds of `$value`s. Example:
//
// ```elvish-transcript
// ~> kind-of lorem [] [&]
// ▶ string
// ▶ list
// ▶ map
// ```
//
// The terminology and definition of "kind" is subject to change.

func kindOf(fm *Frame, args ...interface{}) {
	out := fm.OutputChan()
	for _, a := range args {
		out <- vals.Kind(a)
	}
}

//elvdoc:fn constantly
//
// ```elvish
// constantly $value...
// ```
//
// Output a function that takes no arguments and outputs `$value`s when called.
// Examples:
//
// ```elvish-transcript
// ~> f=(constantly lorem ipsum)
// ~> $f
// ▶ lorem
// ▶ ipsum
// ```
//
// The above example is actually equivalent to simply `f = []{ put lorem ipsum }`;
// it is most useful when the argument is **not** a literal value, e.g.
//
// ```elvish-transcript
// ~> f = (constantly (uname))
// ~> $f
// ▶ Darwin
// ~> $f
// ▶ Darwin
// ```
//
// The above code only calls `uname` once, while if you do `f = []{ put (uname) }`,
// every time you invoke `$f`, `uname` will be called.
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/constantly).

func constantly(args ...interface{}) Callable {
	// TODO(xiaq): Repr of this function is not right.
	return NewGoFn(
		"created by constantly",
		func(fm *Frame) {
			out := fm.OutputChan()
			for _, v := range args {
				out <- v
			}
		},
	)
}

//elvdoc:fn resolve
//
// ```elvish
// resolve $command
// ```
//
// Output what `$command` resolves to in symbolic form. Command resolution is
// described in the [language reference](language.html#ordinary-command).
//
// Example:
//
// ```elvish-transcript
// ~> resolve echo
// ▶ <builtin echo>
// ~> fn f { }
// ~> resolve f
// ▶ <closure 0xc4201c24d0>
// ~> resolve cat
// ▶ <external cat>
// ```

func resolve(fm *Frame, head string) string {
	special, fnRef := resolveCmdHeadInternally(fm, head, nil)
	switch {
	case special != nil:
		return "special"
	case fnRef != nil:
		return "$" + head + FnSuffix
	default:
		return "(external " + parse.Quote(head) + ")"
	}
}

//elvdoc:fn eval
//
// ```elvish
// eval $code &ns=$nil
// ```
//
// Evaluates `$code`, which should be a string. The evaluation happens in the
// namespace specified by the `&ns` option. If it is `$nil` (the default), a
// fresh empty namespace is created.
//
// If `$code` fails to parse or compile, the parse error or compilation error is
// raised as an exception.
//
// Examples:
//
// ```elvish-transcript
// ~> eval 'put x'
// ▶ x
// ~> ns = (ns [&x=initial])
// ~> eval 'put $x; x = altered; put $x' &ns=$ns
// ▶ initial
// ▶ altered
// ~> put $ns[x]
// ▶ altered
// ```
//
// NOTE: Unlike the `eval` found in many other dynamic languages, `eval` cannot
// affect the current namespace:
//
// ```elvish-transcript
// ~> eval 'x = value'
// ~> put $x
// compilation error: variable $x not found
// [tty 4], line 1: put $x
// ```

type evalOpts struct{ Ns *Ns }

func (*evalOpts) SetDefaultOptions() {}

func eval(fm *Frame, opts evalOpts, code string) error {
	src := parse.Source{Name: fmt.Sprintf("[eval %d]", nextEvalCount()), Code: code}
	ns := opts.Ns
	if ns == nil {
		ns = new(Ns)
	}
	return evalInner(fm, src, ns, fm.traceback)
}

// Used to generate unique names for each source passed to eval.
var (
	evalCount      int
	evalCountMutex sync.Mutex
)

func nextEvalCount() int {
	evalCountMutex.Lock()
	defer evalCountMutex.Unlock()
	evalCount++
	return evalCount
}

//elvdoc:fn use-mod
//
// ```elvish
// use-mod $use-spec
// ```
//
// Imports a module, and outputs the namespace for the module.
//
// Most code should use the [use](language.html#importing-modules-with-use)
// special command instead.
//
// Examples:
//
// ```elvish-transcript
// ~> echo 'x = value' > a.elv
// ~> put (use-mod ./a)[x]
// ▶ value
// ```

func useMod(fm *Frame, spec string) (*Ns, error) {
	return use(fm, spec, fm.traceback)
}

//elvdoc:fn -source
//
// ```elvish
// -source $filename
// ```
//
// Read the named file, and evaluate it in a temporary namespace built from the
// current local and up scope.
//
// Examples:
//
// ```elvish-transcript
// ~> cat x.elv
// echo 'executing x.elv'
// foo = bar
// ~> -source x.elv
// executing x.elv
// ~> echo $foo
// bar
// ```
//
// Since the file is evaluated in a temporary namespace, any modifications to
// the namespace itself - creation of variables and deletion of variables - do
// not affect the code calling `-source`. For example:
//
// ```elvish-transcript
// ~> echo 'foo = lorem' > a.elv
// ~> -source a.elv
// ~> put $foo
// compilation error: 4-8 in [tty]: variable $foo not found
// compilation error: variable $foo not found
// [tty 3], line 1: put $foo
// ```
//
// However, the file may mutate variables that already exist, and such mutations
// are persisted:
//
// ```elvish-transcript
// ~> foo = lorem
// ~> echo 'foo = ipsum' > a.elv
// ~> -source a.elv
// ~> put $foo
// ▶ ipsum
// ```

func source(fm *Frame, fname string) error {
	code, err := readFileUTF8(fname)
	if err != nil {
		return err
	}
	src := parse.Source{Name: fname, Code: code, IsFile: true}
	tree, err := parse.ParseWithDeprecation(src, fm.ErrorFile())
	if err != nil {
		return err
	}
	// Amalgamate the up and local scope into a new scope to use as the global
	// scope to evaluate the code in.
	g := amalgamateNs(fm.local, fm.up)
	op, err := compile(fm.Builtin.static(), g.static(), tree, fm.ErrorFile())
	if err != nil {
		return err
	}
	newFm := fm.fork("[-source]")
	newFm.local = g
	newFm.srcMeta = src
	return op.Exec(newFm)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

func amalgamateNs(local, up *Ns) *Ns {
	slots := append([]vars.Var(nil), local.slots...)
	names := append([]string(nil), local.names...)
	for i := range up.slots {
		if local.lookup(up.names[i]) == -1 {
			slots = append(slots, up.slots[i])
			names = append(names, up.names[i])
		}
	}
	return &Ns{slots, names}
}

// TimeAfter is used by the sleep command to obtain a channel that is delivered
// a value after the specified time.
//
// It is a variable to allow for unit tests to efficiently test the behavior of
// the `sleep` command, both by eliminating an actual sleep and verifying the
// duration was properly parsed.
var TimeAfter = func(fm *Frame, d time.Duration) <-chan time.Time {
	return time.After(d)
}

//elvdoc:fn sleep
//
// ```elvish
// sleep $duration
// ```
//
// Pauses for at least the specified duration. The actual pause duration depends
// on the system.
//
// This only affects the current Elvish context. It does not affect any other
// contexts that might be executing in parallel as a consequence of a command
// such as [`peach`](#peach).
//
// A duration can be a simple [number](../language.html#number) (with optional
// fractional value) without an explicit unit suffix, with an implicit unit of
// seconds.
//
// A duration can also be a string written as a sequence of decimal numbers,
// each with optional fraction, plus a unit suffix. For example, "300ms",
// "1.5h" or "1h45m7s". Valid time units are "ns", "us" (or "µs"), "ms", "s",
// "m", "h".
//
// Passing a negative duration causes an exception; this is different from the
// typical BSD or GNU `sleep` command that silently exits with a success status
// without pausing when given a negative duration.
//
// See the [Go documentation](https://golang.org/pkg/time/#ParseDuration) for
// more information about how durations are parsed.
//
// Examples:
//
// ```elvish-transcript
// ~> sleep 0.1    # sleeps 0.1 seconds
// ~> sleep 100ms  # sleeps 0.1 seconds
// ~> sleep 1.5m   # sleeps 1.5 minutes
// ~> sleep 1m30s  # sleeps 1.5 minutes
// ~> sleep -1
// Exception: sleep duration must be >= zero
// [tty 8], line 1: sleep -1
// ```

func sleep(fm *Frame, duration interface{}) error {
	var d time.Duration

	switch duration := duration.(type) {
	case float64:
		d = time.Duration(float64(time.Second) * duration)
	case string:
		f, err := strconv.ParseFloat(duration, 64)
		if err == nil { // it's a simple number assumed to have units == seconds
			d = time.Duration(float64(time.Second) * f)
		} else {
			d, err = time.ParseDuration(duration)
			if err != nil {
				return errors.New("invalid sleep duration")
			}
		}
	default:
		return errors.New("invalid sleep duration")
	}

	if d < 0 {
		return fmt.Errorf("sleep duration must be >= zero")
	}

	select {
	case <-fm.Interrupts():
		return ErrInterrupted
	case <-TimeAfter(fm, d):
		return nil
	}
}

//elvdoc:fn time
//
// ```elvish
// time &on-end=$nil $callable
// ```
//
// Runs the callable, and call `$on-end` with the duration it took, as a
// number in seconds. If `$on-end` is `$nil` (the default), prints the
// duration in human-readable form.
//
// If `$callable` throws an exception, the exception is propagated after the
// on-end or default printing is done.
//
// If `$on-end` throws an exception, it is propagated, unless `$callable` has
// already thrown an exception.
//
// Example:
//
// ```elvish-transcript
// ~> time { sleep 1 }
// 1.006060647s
// ~> time { sleep 0.01 }
// 1.288977ms
// ~> t = ''
// ~> time &on-end=[x]{ t = $x } { sleep 1 }
// ~> put $t
// ▶ (float64 1.000925004)
// ~> time &on-end=[x]{ t = $x } { sleep 0.01 }
// ~> put $t
// ▶ (float64 0.011030208)
// ```

type timeOpt struct{ OnEnd Callable }

func (o *timeOpt) SetDefaultOptions() {}

func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
	t0 := time.Now()
	err := f.Call(fm, NoArgs, NoOpts)
	t1 := time.Now()

	dt := t1.Sub(t0)
	if opts.OnEnd != nil {
		newFm := fm.fork("on-end callback of time")
		errCb := opts.OnEnd.Call(newFm, []interface{}{dt.Seconds()}, NoOpts)
		if err == nil {
			err = errCb
		}
	} else {
		fmt.Fprintln(fm.OutputFile(), dt)
	}

	return err
}

//elvdoc:fn -ifaddrs
//
// ```elvish
// -ifaddrs
// ```
//
// Output all IP addresses of the current host.
//
// This should be part of a networking module instead of the builtin module.

func _ifaddrs(fm *Frame) error {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return err
	}
	out := fm.OutputChan()
	for _, addr := range addrs {
		out <- addr.String()
	}
	return nil
}
<|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 strings

import (
	"os"
	"utf8"
)

// A Reader satisfies calls to Read, ReadByte, and ReadRune by
// reading from a string.
type Reader string

func (r *Reader) Read(b []byte) (n int, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, os.EOF
	}
	for n < len(s) && n < len(b) {
		b[n] = s[n]
		n++
	}
	*r = s[n:]
	return
}

func (r *Reader) ReadByte() (b byte, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, os.EOF
	}
	b = s[0]
	*r = s[1:]
	return
}

// ReadRune reads and returns the next UTF-8-encoded
// Unicode code point from the buffer.
// If no bytes are available, the error returned is os.EOF.
// If the bytes are an erroneous UTF-8 encoding, it
// consumes one byte and returns U+FFFD, 1.
func (r *Reader) ReadRune() (rune int, size int, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, 0, os.EOF
	}
	c := s[0]
	if c < utf8.RuneSelf {
		*r = s[1:]
		return int(c), 1, nil
	}
	rune, size = utf8.DecodeRuneInString(string(s))
	*r = s[size:]
	return
}

// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return (*Reader)(&s) }
<commit_msg>strings: make Reader.Read use copy instead of an explicit loop.<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 strings

import (
	"os"
	"utf8"
)

// A Reader satisfies calls to Read, ReadByte, and ReadRune by
// reading from a string.
type Reader string

func (r *Reader) Read(b []byte) (n int, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, os.EOF
	}
	n = copy(b, s)
	*r = s[n:]
	return
}

func (r *Reader) ReadByte() (b byte, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, os.EOF
	}
	b = s[0]
	*r = s[1:]
	return
}

// ReadRune reads and returns the next UTF-8-encoded
// Unicode code point from the buffer.
// If no bytes are available, the error returned is os.EOF.
// If the bytes are an erroneous UTF-8 encoding, it
// consumes one byte and returns U+FFFD, 1.
func (r *Reader) ReadRune() (rune int, size int, err os.Error) {
	s := *r
	if len(s) == 0 {
		return 0, 0, os.EOF
	}
	c := s[0]
	if c < utf8.RuneSelf {
		*r = s[1:]
		return int(c), 1, nil
	}
	rune, size = utf8.DecodeRuneInString(string(s))
	*r = s[size:]
	return
}

// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return (*Reader)(&s) }
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"io/ioutil"
	"strconv"
)

func init() {
	// Attempt to read the actual limit from procfs and fail silently.
	b, err := ioutil.ReadFile("/proc/sys/net/core/rmem_max")
	if err == nil {
		readBuffer, err = strconv.Atoi(string(b))
	}
	if err != nil {
		// sysctl net.core.rmem_max -> 212992
		readBuffer = 212992
	}
}
<commit_msg>Wrong variable<commit_after>// +build linux

package main

import (
	"io/ioutil"
	"strconv"
)

func init() {
	// Attempt to read the actual limit from procfs and fail silently.
	b, err := ioutil.ReadFile("/proc/sys/net/core/rmem_max")
	if err == nil {
		readSize, err = strconv.Atoi(string(b))
	}
	if err != nil {
		// sysctl net.core.rmem_max -> 212992
		readSize = 212992
	}
}
<|endoftext|>