<commit_before>//
// upload.go
// Copyright(c)2014-2015 Google, Inc.
//
// 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 gdrive

import (
	"bytes"
	"fmt"
	"google.golang.org/api/drive/v2"
	"google.golang.org/api/googleapi"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

// UploadFileContents uploads the file contents given by the io.Reader to
// the given File.  The upload may fail due to various transient network
// errors; as such, the caller should check to see if a non-nil returned
// error code is a RetryHTTPTransmitError.  In this case, it should try
// again, providing a new io.Reader that points to the start of the file.
// The 'try' parameter should track how many times this function has been
// called to try to upload the given file due to RetryHTTPTransmitErrors.
func (gd *GDrive) UploadFileContents(f *File, contentsReader io.Reader,
	length int64, try int) error {
	// Limit upload bandwidth, if requested..
	contentsReader = makeLimitedUploadReader(ioutil.NopCloser(contentsReader))

	// Get the PUT request for the upload.
	req, err := prepareUploadPUT(f.Id, contentsReader, length)
	if err != nil {
		return err
	}
	if req == nil {
		// Empty file--we're done.
		return nil
	}

	// And send it off...
	resp, err := gd.client.Do(req)
	if resp != nil {
		defer googleapi.CloseBody(resp)
	}

	switch gd.handleHTTPResponse(resp, err, try) {
	case Success:
		gd.debug("Success for %s: code %d", f.Path, resp.StatusCode)
		return nil
	case Fail:
		if err == nil {
			log.Fatalf("nil err but fail? resp %v", *resp)
		}
		return err
	case Retry:
		// Otherwise tell the caller to please set up the reader, etc.,
		// again and retry...
		if resp != nil {
			b, _ := ioutil.ReadAll(resp.Body)
			return RetryHTTPTransmitError{StatusCode: resp.StatusCode,
				StatusBody: string(b)}
		}
		return RetryHTTPTransmitError{StatusCode: 500, StatusBody: err.Error()}
	default:
		panic("Unhandled HTTPResult value in switch")
	}
}

func prepareUploadPUT(id string, contentsReader io.Reader,
	length int64) (*http.Request, error) {
	params := make(url.Values)
	params.Set("uploadType", "media")

	urls := fmt.Sprintf("https://www.googleapis.com/upload/drive/v2/files/%s",
		url.QueryEscape(id))
	urls += "?" + params.Encode()

	contentsReader, contentType, err := detectContentType(contentsReader)
	if err != nil {
		return nil, err
	}

	req, _ := http.NewRequest("PUT", urls, contentsReader)
	googleapi.SetOpaque(req.URL)
	req.ContentLength = length
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("User-Agent", "skicka/0.1")

	return req, nil
}

func detectContentType(contentsReader io.Reader) (io.Reader, string, error) {
	// Grab the start of the contents so that we can try to identify
	// the content type.
	contentsHeader := make([]byte, 512)
	headerLength, err := contentsReader.Read(contentsHeader)
	if err != nil {
		if err.Error() == "EOF" {
			// Empty file; this is fine, and we're done.
			return nil, "", nil
		}
		return nil, "", err
	}
	contentType := http.DetectContentType(contentsHeader)

	// Reconstruct a new Reader that returns the same byte stream
	// as the original one, effectively pasting the bytes we read for
	// the content-type identification to the start of what remains in
	// the original io.Reader.
	contentsReader = io.MultiReader(bytes.NewReader(contentsHeader[:headerLength]),
		contentsReader)

	return contentsReader, contentType, nil
}

func (gd *GDrive) getResumableUploadURI(f *drive.File, contentType string,
	length int64) (string, error) {
	params := make(url.Values)
	params.Set("uploadType", "resumable")

	urls := fmt.Sprintf("https://www.googleapis.com/upload/drive/v2/files/%s",
		f.Id)
	urls += "?" + params.Encode()

	body, err := googleapi.WithoutDataWrapper.JSONReader(f)
	if err != nil {
		return "", err
	}

	req, _ := http.NewRequest("PUT", urls, body)
	req.Header.Set("X-Upload-Content-Length", fmt.Sprintf("%d", length))
	req.Header.Set("X-Upload-Content-Type", contentType)
	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set("User-Agent", "skicka/0.1")
	// We actually don't need any content in the request, since we're
	// PUTing to an existing file.

	for try := 0; ; try++ {
		gd.debug("Trying to get session URI")
		resp, err := gd.client.Do(req)
		if resp != nil && resp.Body != nil {
			defer resp.Body.Close()
		}

		if err == nil && resp != nil && resp.StatusCode == 200 {
			uri := resp.Header["Location"][0]
			gd.debug("Got resumable upload URI %s", uri)
			return uri, nil
		}
		if err != nil {
			gd.debug("getResumableUploadURI: %v", err)
		}
		if resp != nil {
			b, _ := ioutil.ReadAll(resp.Body)
			gd.debug("getResumableUploadURI status %d\n"+
				"Resp: %+v\nBody: %s", resp.StatusCode, *resp, b)
		}
		if try == maxRetries {
			// Give up...
			return "", err
		}

		gd.exponentialBackoff(try, resp, err)
	}
}

// In certain error cases, we need to go back and query Drive as to how
// much of a file has been successfully uploaded (and thence where we
// should start for the next chunk.)  This function generates that query
// and updates the provided *currentOffset parameter with the result.
func (gd *GDrive) getCurrentChunkStart(sessionURI string, contentLength int64,
	currentOffset *int64) (HTTPResponseResult, error) {
	var err error
	for r := 0; r < maxRetries; r++ {
		req, _ := http.NewRequest("PUT", sessionURI, nil)
		req.Header.Set("Content-Range", fmt.Sprintf("bytes */%d", contentLength))
		req.Header.Set("Content-Length", "0")
		req.ContentLength = 0
		req.Header.Set("User-Agent", "skicka/0.1")
		resp, err := gd.client.Do(req)

		if resp == nil {
			gd.debug("get current chunk start err %v", err)
			gd.exponentialBackoff(r, resp, err)
			continue
		}

		defer resp.Body.Close()

		if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
			// 200 or 201 here says we're actually all done
			gd.debug("All done: %d from get content-range response",
				resp.StatusCode)
			return Success, nil
		} else if resp.StatusCode == 308 {
			*currentOffset, err = updateStartFromResponse(resp)
			if err != nil {
				return Fail, err
			}
			gd.debug("Updated start to %d after 308 from get "+
				"content-range...", *currentOffset)
			return Retry, nil
		}
	}
	gd.debug("couldn't recover from 503...")
	return Fail, err
}

// The response we get back from uploading a file chunk includes a "Range"
// field, which gives the range (inclusive!) of bytes that actually were
// successfully uploaded; the ending byte offset may be before the end of
// the range we tried to upload, if there was an error partway through.
// This function returns this offset, so that the next chunk upload can
// start at the right place.
func updateStartFromResponse(resp *http.Response) (int64, error) {
	if rangeString, ok := resp.Header["Range"]; ok && len(rangeString) > 0 {
		var rangeStart, rangeEnd int64
		fmt.Sscanf(rangeString[0], "bytes=%d-%d", &rangeStart, &rangeEnd)
		return rangeEnd + 1, nil
	}
	return 0, fmt.Errorf("Malformed HTTP response to get range %v", *resp)
}

// When we upload a file chunk, a variety of responses may come back from
// the server, ranging from permanent errors to transient errors, to
// success codes.  This function processes the http.Response and maps it to
// a HTTPResponseResult code.  It also may update *try, the conut of how
// many times we've tried in a row to upload a chunk, *start, the current
// offset into the file being uploaded, and *sessionURI, the URI to which
// chunks for the file should be uploaded to.
func (gd *GDrive) handleResumableUploadResponse(resp *http.Response, err error,
	f *drive.File, contentType string, contentLength int64, try *int,
	currentOffset *int64, sessionURI *string) (HTTPResponseResult, error) {
	if *try == maxRetries {
		if err != nil {
			return Fail, fmt.Errorf("giving up after %d retries: %v",
				maxRetries, err)
		} else if resp.StatusCode == http.StatusForbidden {
			return Fail, fmt.Errorf("giving up after %d retries: "+
				"rate limit exceeded", maxRetries)
		} else {
			return Fail, fmt.Errorf("giving up after %d retries: %s",
				maxRetries, resp.Status)
		}
	}

	// Serious error (e.g. connection reset) where we didn't even get a
	// HTTP response back from the server.  Try again (a few times).
	if err != nil {
		gd.debug("handleResumableUploadResponse error %v", err)
		gd.exponentialBackoff(*try, resp, err)
		return Retry, nil
	}

	gd.debug("got status %d from chunk for file %s: %v", resp.StatusCode,
		f.Id, resp)

	switch {
	case resp.StatusCode >= 200 && resp.StatusCode <= 299:
		// Successfully uploaded the entire file.
		return Success, nil

	case resp.StatusCode == 308:
		// This is the expected response when a chunk was uploaded
		// successfully, but there are still more chunks to do
		// before we're done.
		*currentOffset, err = updateStartFromResponse(resp)
		if err != nil {
			return Fail, err
		}
		*try = 0
		gd.debug("Updated currentOffset to %d after 308", *currentOffset)
		return Retry, nil

	case resp.StatusCode == http.StatusNotFound:
		// The upload URI has expired; we need to refresh it. (It
		// has a ~24 hour lifetime.)
		*sessionURI, err = gd.getResumableUploadURI(f, contentType,
			contentLength)
		gd.debug("Got %v after updating URI from 404...", err)
		if err != nil {
			return Fail, err
		}

		// Use the new URI to find the offset to start at.
		*try = 0
		return gd.getCurrentChunkStart(*sessionURI, contentLength,
			currentOffset)

	case resp.StatusCode >= 500 && resp.StatusCode <= 599:
		gd.debug("5xx response")
		return gd.getCurrentChunkStart(*sessionURI, contentLength, currentOffset)

	default:
		gd.exponentialBackoff(*try, resp, err)
		return Retry, nil
	}
}

// UploadFileContentsResumable uses the resumable upload protocol to upload
// the file contents from the given Reader to the given *drive.File on
// Google Drive.  This approach is more expensive than UploadFileContents()
// for files under a few megabytes, but is helpful for large files in that
// it's more robust to transient errors and can handle OAuth2 token
// refreshes in the middle of an upload, unlike the regular approach.
func (gd *GDrive) UploadFileContentsResumable(file *File,
	contentsReader io.Reader, contentLength int64) error {
	contentsReader, contentType, err := detectContentType(contentsReader)
	if err != nil {
		return err
	}

	sessionURI, err := gd.getResumableUploadURI(file.driveFile(), contentType,
		contentLength)
	if err != nil {
		return err
	}

	// TODO: what is a reasonable default here? Must be 256kB minimum.
	chunkSize := 1024 * 1024

	seekableReader := makeSomewhatSeekableReader(contentsReader, 2*chunkSize)

	// Upload the file in chunks of size chunkSize (or smaller, for the
	// very last chunk).
	for currentOffset, try := int64(0), 0; currentOffset < contentLength; try++ {
		end := currentOffset + int64(chunkSize)
		if end > contentLength {
			end = contentLength
		}
		gd.debug("%s: uploading chunk %d - %d...", file.Path,
			currentOffset, end)

		// We should usually already be at the current offset; this
		// seek should be a no-op except in cases where the
		// previous chunk had an error.
		err = seekableReader.SeekTo(currentOffset)
		if err != nil {
			return err
		}

		// Only allow the current range of bytes to be uploaded
		// with this PUT.
		var body io.Reader = &io.LimitedReader{
			R: seekableReader,
			N: end - currentOffset,
		}
		body = makeLimitedUploadReader(ioutil.NopCloser(body))

		req, err := http.NewRequest("PUT", sessionURI, body)
		if err != nil {
			return err
		}
		req.ContentLength = int64(end - currentOffset)
		req.Header.Set("Content-Type", contentType)
		req.Header.Set("Content-Range",
			fmt.Sprintf("bytes %d-%d/%d", currentOffset, end-1, contentLength))
		req.Header.Set("User-Agent", "skicka/0.1")

		// Actually (try to) upload the chunk.
		resp, err := gd.client.Do(req)

		status, err := gd.handleResumableUploadResponse(resp, err,
			file.driveFile(), contentType, contentLength, &try, &currentOffset,
			&sessionURI)

		if resp != nil {
			googleapi.CloseBody(resp)
		}
		if status == Fail {
			return err
		} else if status == Success {
			// The entire file has been uploaded successfully.
			return nil
		}

		// Go around again and do the next chunk...
	}

	// This should perhaps be a panic, as if we are able to upload all
	// of the data but then the Drive API doesn't give us a 2xx reply
	// with the last chunk, then something is really broken.
	return fmt.Errorf("uploaded entire file but didn't get 2xx status on last chunk")
}
<commit_msg>Remove googleapi.SetOpaque call.<commit_after>//
// upload.go
// Copyright(c)2014-2015 Google, Inc.
//
// 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 gdrive

import (
	"bytes"
	"fmt"
	"google.golang.org/api/drive/v2"
	"google.golang.org/api/googleapi"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

// UploadFileContents uploads the file contents given by the io.Reader to
// the given File.  The upload may fail due to various transient network
// errors; as such, the caller should check to see if a non-nil returned
// error code is a RetryHTTPTransmitError.  In this case, it should try
// again, providing a new io.Reader that points to the start of the file.
// The 'try' parameter should track how many times this function has been
// called to try to upload the given file due to RetryHTTPTransmitErrors.
func (gd *GDrive) UploadFileContents(f *File, contentsReader io.Reader,
	length int64, try int) error {
	// Limit upload bandwidth, if requested..
	contentsReader = makeLimitedUploadReader(ioutil.NopCloser(contentsReader))

	// Get the PUT request for the upload.
	req, err := prepareUploadPUT(f.Id, contentsReader, length)
	if err != nil {
		return err
	}
	if req == nil {
		// Empty file--we're done.
		return nil
	}

	// And send it off...
	resp, err := gd.client.Do(req)
	if resp != nil {
		defer googleapi.CloseBody(resp)
	}

	switch gd.handleHTTPResponse(resp, err, try) {
	case Success:
		gd.debug("Success for %s: code %d", f.Path, resp.StatusCode)
		return nil
	case Fail:
		if err == nil {
			log.Fatalf("nil err but fail? resp %v", *resp)
		}
		return err
	case Retry:
		// Otherwise tell the caller to please set up the reader, etc.,
		// again and retry...
		if resp != nil {
			b, _ := ioutil.ReadAll(resp.Body)
			return RetryHTTPTransmitError{StatusCode: resp.StatusCode,
				StatusBody: string(b)}
		}
		return RetryHTTPTransmitError{StatusCode: 500, StatusBody: err.Error()}
	default:
		panic("Unhandled HTTPResult value in switch")
	}
}

func prepareUploadPUT(id string, contentsReader io.Reader,
	length int64) (*http.Request, error) {
	params := make(url.Values)
	params.Set("uploadType", "media")

	urls := fmt.Sprintf("https://www.googleapis.com/upload/drive/v2/files/%s",
		url.QueryEscape(id))
	urls += "?" + params.Encode()

	contentsReader, contentType, err := detectContentType(contentsReader)
	if err != nil {
		return nil, err
	}

	req, _ := http.NewRequest("PUT", urls, contentsReader)
	req.ContentLength = length
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("User-Agent", "skicka/0.1")

	return req, nil
}

func detectContentType(contentsReader io.Reader) (io.Reader, string, error) {
	// Grab the start of the contents so that we can try to identify
	// the content type.
	contentsHeader := make([]byte, 512)
	headerLength, err := contentsReader.Read(contentsHeader)
	if err != nil {
		if err.Error() == "EOF" {
			// Empty file; this is fine, and we're done.
			return nil, "", nil
		}
		return nil, "", err
	}
	contentType := http.DetectContentType(contentsHeader)

	// Reconstruct a new Reader that returns the same byte stream
	// as the original one, effectively pasting the bytes we read for
	// the content-type identification to the start of what remains in
	// the original io.Reader.
	contentsReader = io.MultiReader(bytes.NewReader(contentsHeader[:headerLength]),
		contentsReader)

	return contentsReader, contentType, nil
}

func (gd *GDrive) getResumableUploadURI(f *drive.File, contentType string,
	length int64) (string, error) {
	params := make(url.Values)
	params.Set("uploadType", "resumable")

	urls := fmt.Sprintf("https://www.googleapis.com/upload/drive/v2/files/%s",
		f.Id)
	urls += "?" + params.Encode()

	body, err := googleapi.WithoutDataWrapper.JSONReader(f)
	if err != nil {
		return "", err
	}

	req, _ := http.NewRequest("PUT", urls, body)
	req.Header.Set("X-Upload-Content-Length", fmt.Sprintf("%d", length))
	req.Header.Set("X-Upload-Content-Type", contentType)
	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set("User-Agent", "skicka/0.1")
	// We actually don't need any content in the request, since we're
	// PUTing to an existing file.

	for try := 0; ; try++ {
		gd.debug("Trying to get session URI")
		resp, err := gd.client.Do(req)
		if resp != nil && resp.Body != nil {
			defer resp.Body.Close()
		}

		if err == nil && resp != nil && resp.StatusCode == 200 {
			uri := resp.Header["Location"][0]
			gd.debug("Got resumable upload URI %s", uri)
			return uri, nil
		}
		if err != nil {
			gd.debug("getResumableUploadURI: %v", err)
		}
		if resp != nil {
			b, _ := ioutil.ReadAll(resp.Body)
			gd.debug("getResumableUploadURI status %d\n"+
				"Resp: %+v\nBody: %s", resp.StatusCode, *resp, b)
		}
		if try == maxRetries {
			// Give up...
			return "", err
		}

		gd.exponentialBackoff(try, resp, err)
	}
}

// In certain error cases, we need to go back and query Drive as to how
// much of a file has been successfully uploaded (and thence where we
// should start for the next chunk.)  This function generates that query
// and updates the provided *currentOffset parameter with the result.
func (gd *GDrive) getCurrentChunkStart(sessionURI string, contentLength int64,
	currentOffset *int64) (HTTPResponseResult, error) {
	var err error
	for r := 0; r < maxRetries; r++ {
		req, _ := http.NewRequest("PUT", sessionURI, nil)
		req.Header.Set("Content-Range", fmt.Sprintf("bytes */%d", contentLength))
		req.Header.Set("Content-Length", "0")
		req.ContentLength = 0
		req.Header.Set("User-Agent", "skicka/0.1")
		resp, err := gd.client.Do(req)

		if resp == nil {
			gd.debug("get current chunk start err %v", err)
			gd.exponentialBackoff(r, resp, err)
			continue
		}

		defer resp.Body.Close()

		if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
			// 200 or 201 here says we're actually all done
			gd.debug("All done: %d from get content-range response",
				resp.StatusCode)
			return Success, nil
		} else if resp.StatusCode == 308 {
			*currentOffset, err = updateStartFromResponse(resp)
			if err != nil {
				return Fail, err
			}
			gd.debug("Updated start to %d after 308 from get "+
				"content-range...", *currentOffset)
			return Retry, nil
		}
	}
	gd.debug("couldn't recover from 503...")
	return Fail, err
}

// The response we get back from uploading a file chunk includes a "Range"
// field, which gives the range (inclusive!) of bytes that actually were
// successfully uploaded; the ending byte offset may be before the end of
// the range we tried to upload, if there was an error partway through.
// This function returns this offset, so that the next chunk upload can
// start at the right place.
func updateStartFromResponse(resp *http.Response) (int64, error) {
	if rangeString, ok := resp.Header["Range"]; ok && len(rangeString) > 0 {
		var rangeStart, rangeEnd int64
		fmt.Sscanf(rangeString[0], "bytes=%d-%d", &rangeStart, &rangeEnd)
		return rangeEnd + 1, nil
	}
	return 0, fmt.Errorf("Malformed HTTP response to get range %v", *resp)
}

// When we upload a file chunk, a variety of responses may come back from
// the server, ranging from permanent errors to transient errors, to
// success codes.  This function processes the http.Response and maps it to
// a HTTPResponseResult code.  It also may update *try, the conut of how
// many times we've tried in a row to upload a chunk, *start, the current
// offset into the file being uploaded, and *sessionURI, the URI to which
// chunks for the file should be uploaded to.
func (gd *GDrive) handleResumableUploadResponse(resp *http.Response, err error,
	f *drive.File, contentType string, contentLength int64, try *int,
	currentOffset *int64, sessionURI *string) (HTTPResponseResult, error) {
	if *try == maxRetries {
		if err != nil {
			return Fail, fmt.Errorf("giving up after %d retries: %v",
				maxRetries, err)
		} else if resp.StatusCode == http.StatusForbidden {
			return Fail, fmt.Errorf("giving up after %d retries: "+
				"rate limit exceeded", maxRetries)
		} else {
			return Fail, fmt.Errorf("giving up after %d retries: %s",
				maxRetries, resp.Status)
		}
	}

	// Serious error (e.g. connection reset) where we didn't even get a
	// HTTP response back from the server.  Try again (a few times).
	if err != nil {
		gd.debug("handleResumableUploadResponse error %v", err)
		gd.exponentialBackoff(*try, resp, err)
		return Retry, nil
	}

	gd.debug("got status %d from chunk for file %s: %v", resp.StatusCode,
		f.Id, resp)

	switch {
	case resp.StatusCode >= 200 && resp.StatusCode <= 299:
		// Successfully uploaded the entire file.
		return Success, nil

	case resp.StatusCode == 308:
		// This is the expected response when a chunk was uploaded
		// successfully, but there are still more chunks to do
		// before we're done.
		*currentOffset, err = updateStartFromResponse(resp)
		if err != nil {
			return Fail, err
		}
		*try = 0
		gd.debug("Updated currentOffset to %d after 308", *currentOffset)
		return Retry, nil

	case resp.StatusCode == http.StatusNotFound:
		// The upload URI has expired; we need to refresh it. (It
		// has a ~24 hour lifetime.)
		*sessionURI, err = gd.getResumableUploadURI(f, contentType,
			contentLength)
		gd.debug("Got %v after updating URI from 404...", err)
		if err != nil {
			return Fail, err
		}

		// Use the new URI to find the offset to start at.
		*try = 0
		return gd.getCurrentChunkStart(*sessionURI, contentLength,
			currentOffset)

	case resp.StatusCode >= 500 && resp.StatusCode <= 599:
		gd.debug("5xx response")
		return gd.getCurrentChunkStart(*sessionURI, contentLength, currentOffset)

	default:
		gd.exponentialBackoff(*try, resp, err)
		return Retry, nil
	}
}

// UploadFileContentsResumable uses the resumable upload protocol to upload
// the file contents from the given Reader to the given *drive.File on
// Google Drive.  This approach is more expensive than UploadFileContents()
// for files under a few megabytes, but is helpful for large files in that
// it's more robust to transient errors and can handle OAuth2 token
// refreshes in the middle of an upload, unlike the regular approach.
func (gd *GDrive) UploadFileContentsResumable(file *File,
	contentsReader io.Reader, contentLength int64) error {
	contentsReader, contentType, err := detectContentType(contentsReader)
	if err != nil {
		return err
	}

	sessionURI, err := gd.getResumableUploadURI(file.driveFile(), contentType,
		contentLength)
	if err != nil {
		return err
	}

	// TODO: what is a reasonable default here? Must be 256kB minimum.
	chunkSize := 1024 * 1024

	seekableReader := makeSomewhatSeekableReader(contentsReader, 2*chunkSize)

	// Upload the file in chunks of size chunkSize (or smaller, for the
	// very last chunk).
	for currentOffset, try := int64(0), 0; currentOffset < contentLength; try++ {
		end := currentOffset + int64(chunkSize)
		if end > contentLength {
			end = contentLength
		}
		gd.debug("%s: uploading chunk %d - %d...", file.Path,
			currentOffset, end)

		// We should usually already be at the current offset; this
		// seek should be a no-op except in cases where the
		// previous chunk had an error.
		err = seekableReader.SeekTo(currentOffset)
		if err != nil {
			return err
		}

		// Only allow the current range of bytes to be uploaded
		// with this PUT.
		var body io.Reader = &io.LimitedReader{
			R: seekableReader,
			N: end - currentOffset,
		}
		body = makeLimitedUploadReader(ioutil.NopCloser(body))

		req, err := http.NewRequest("PUT", sessionURI, body)
		if err != nil {
			return err
		}
		req.ContentLength = int64(end - currentOffset)
		req.Header.Set("Content-Type", contentType)
		req.Header.Set("Content-Range",
			fmt.Sprintf("bytes %d-%d/%d", currentOffset, end-1, contentLength))
		req.Header.Set("User-Agent", "skicka/0.1")

		// Actually (try to) upload the chunk.
		resp, err := gd.client.Do(req)

		status, err := gd.handleResumableUploadResponse(resp, err,
			file.driveFile(), contentType, contentLength, &try, &currentOffset,
			&sessionURI)

		if resp != nil {
			googleapi.CloseBody(resp)
		}
		if status == Fail {
			return err
		} else if status == Success {
			// The entire file has been uploaded successfully.
			return nil
		}

		// Go around again and do the next chunk...
	}

	// This should perhaps be a panic, as if we are able to upload all
	// of the data but then the Drive API doesn't give us a 2xx reply
	// with the last chunk, then something is really broken.
	return fmt.Errorf("uploaded entire file but didn't get 2xx status on last chunk")
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 (
	"os"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/nodetemple/nodetemple/common"
	"github.com/nodetemple/nodetemple/nodectl/util"
	"github.com/nodetemple/nodetemple/nodectl/command"
)

func main() {
	app := cli.NewApp()
	app.Name = "nodectl"
	app.Usage = "CLI for an orchestration of CoreOS and Kubernetes cluster"
	app.Version = common.Version
	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "providers, p", Usage: "A comma-separated list of IaaS providers ("+strings.Join(common.AvailableProviders, ",")+") and API keys, format: 'provider:api-key,...'", EnvVar: util.EnvVarConv(app.Name, "providers"),},
		cli.BoolFlag{Name: "debug", Usage: "Print out more debug information to stderr"},
	}
	app.Before = func(c *cli.Context) {
		if c.String("providers") == "" && !c.Bool("help") && !c.Bool("version") {
			util.Err("set at least one provider with a valid API key")
		}
	}
	app.Commands = []cli.Command{
		command.DemoCmd(),
	}
	app.CommandNotFound = func(c *cli.Context, command string) {
		util.Err("unknown command '%v'\nRun '%v help [command]' for usage information", command, c.App.Name)
	}

	app.RunAndExitOnError()
}
<commit_msg>Imported and not used<commit_after>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 (
	"strings"

	"github.com/codegangsta/cli"
	"github.com/nodetemple/nodetemple/common"
	"github.com/nodetemple/nodetemple/nodectl/util"
	"github.com/nodetemple/nodetemple/nodectl/command"
)

func main() {
	app := cli.NewApp()
	app.Name = "nodectl"
	app.Usage = "CLI for an orchestration of CoreOS and Kubernetes cluster"
	app.Version = common.Version
	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "providers, p", Usage: "A comma-separated list of IaaS providers ("+strings.Join(common.AvailableProviders, ",")+") and API keys, format: 'provider:api-key,...'", EnvVar: util.EnvVarConv(app.Name, "providers"),},
		cli.BoolFlag{Name: "debug", Usage: "Print out more debug information to stderr"},
	}
	app.Before = func(c *cli.Context) {
		if c.String("providers") == "" && !c.Bool("help") && !c.Bool("version") {
			util.Err("set at least one provider with a valid API key")
		}
	}
	app.Commands = []cli.Command{
		command.DemoCmd(),
	}
	app.CommandNotFound = func(c *cli.Context, command string) {
		util.Err("unknown command '%v'\nRun '%v help [command]' for usage information", command, c.App.Name)
	}

	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>package factorlib

import (
	"fmt"
	"math/rand"
)

// sieve [-sieverange,sieverange) around mininum point.
const sieverange = 1 << 10

// use an array of this size to do the sieving
const window = 1 << 8

// Records f(x) == product(factors)*remainder
// The values in factors are indexes into the factor base
type sieveResult struct {
	x         bigint
	factors   []uint
	remainder int64
}

func sieveinner(sieve []byte, si []sieveinfo2, threshold byte) []int {
	var r []int
	for i := 0; i < 2*sieverange; i += window {
		// clear sieve
		for j := 0; j < window; j++ {
			sieve[j] = 0
		}
		// increment sieve entries for f(x) that are divisible
		// by each factor base prime.
		for j := range si {
			f := &si[j]
			pk := int(f.pk)
			lg_p := f.lg_p
			j := int(f.off)
			for ; j < window; j += pk {
				sieve[j] += lg_p
			}
			f.off = int32(j - window) // for next time
		}
		for j := 0; j < window; j++ {
			if sieve[j] >= threshold {
				r = append(r, i+j)
			}
		}
	}
	return r
}

// Find values of x for which f(x) = a x^2 + b x + c factors (within one bigprime) over the primes in fb.
// requires: a > 0
func sievesmooth(a, b, c bigint, fb []int64, rnd *rand.Rand) []sieveResult {
	var result []sieveResult

	maxp := fb[len(fb)-1]

	// find approximate zero crossings
	d := b.Square().Sub(a.Mul(c).Lsh(2))
	if d.Sign() < 0 {
		panic("polynomial has no roots")
		// TODO: choose min instead?  Then x = -b/2a
	}
	d = d.SqrtFloor()
	x := b.Neg().Add(d).Div(a).Rsh(1)
	//x2 := b.Neg().Sub(d).Div(a).Rsh(1)
	// TODO: sieve around x2 also? (if d != 0)

	// starting point
	x0 := x.Sub64(sieverange)

	sieve := make([]byte, window) // TODO: cache this?

	var factors []uint

	// find starting points
	si := makeSieveInfo2(a, b, c, x0, fb, rnd)

	// pick threshold
	startf := a.Mul(x0).Add(b).Mul(x0).Add(c)
	threshold := byte(startf.BitLen()) - 2*log2(maxp) // TODO: subtract more?

	// sieve to find any potential smooth f(x)
	res := sieveinner(sieve, si, threshold)

	// check potential results using trial factorization
	for _, i := range res {
		// compute f(x)
		x := x0.Add64(int64(i))
		f := a.Mul(x).Add(b).Mul(x).Add(c)

		// trial divide f by the factor base
		// accumulate factor base indexes of factors
		factors = factors[:0]
		if f.Sign() < 0 {
			factors = append(factors, 0)
			f = f.Neg()
		}
		for k, p := range fb[1:] {
			for f.Mod64(p) == 0 {
				f = f.Div64(p)
				factors = append(factors, uint(k+1))
			}
		}

		// if remainder > B^2, it's too big, might not be prime.
		if f.Cmp64(maxp*maxp) > 0 {
			//fmt.Printf("  false positive y=%d z=%d threshold=%d sieve[i]=%d log2(y)=%d log2(y/z)=%d\n", y, bigz, threshold, sieve[i], y.BitLen(), x.Div(y, bigz).BitLen())
			continue
		}

		result = append(result, sieveResult{x, dup(factors), f.Int64()})
	}
	return result
}

type sieveinfo2 struct {
	pk   int32 // p^k for this factor base entry
	lg_p uint8 // ~log_2(p)
	off  int32 // working offset in sieve array
}

func makeSieveInfo2(a, b, c bigint, start bigint, fb []int64, rnd *rand.Rand) []sieveinfo2 {
	var si []sieveinfo2

	for _, p := range fb[1:] {
		pk := p
		for k := uint(1); k < 2; k++ { // TODO: quadraticModPK
			if pk > fb[len(fb)-1] {
				// Kind of arbitrary, but use powers of p as long as p^k is
				// smaller than than the maximum factor base prime.
				break
			}
			s := start.Mod64(pk)
			for _, r := range quadraticModP(a.Mod64(pk), b.Mod64(pk), c.Mod64(pk), pk, rnd) {
				// find first pk*i+r which is >= start
				off := (r - s + pk) % pk
				si = append(si, sieveinfo2{int32(pk), log2(p), int32(off)})
			}
			pk *= p
		}
	}
	return si
}

func init() {
	factorizers["qs2"] = qs2
}

func qs2(n bigint, rnd *rand.Rand) []bigint {
	// qs does not work for powers of a single prime.  Check that first.
	if f := primepower(n, rnd); f != nil {
		return f
	}

	// first, pick a factor base
	fb, a := makeFactorBase(n)
	if a != 0 {
		return []bigint{Big(a), n.Div64(a)}
	}

	for _, r := range sievesmooth(Big(1), Big(0), n.Neg(), fb, rnd) {
		fmt.Printf("f(%d)= prod %v * %d\n", r.x, r.factors, r.remainder)
	}
	return nil
}
<commit_msg>Cleanup.<commit_after>package factorlib

import (
	"fmt"
	"math/rand"
)

// sieve [-sieverange,sieverange) around mininum point.
const sieverange = 1 << 10

// use an array of this size to do the sieving
const window = 1 << 8

// Records f(x) == product(factors)*remainder
// The values in factors are indexes into the factor base
type sieveResult struct {
	x         bigint
	factors   []uint
	remainder int64
}

func sieveinner(sieve []byte, si []sieveinfo2, threshold byte) []int {
	var r []int
	for i := 0; i < 2*sieverange; i += window {
		// clear sieve
		for j := 0; j < window; j++ {
			sieve[j] = 0
		}
		// increment sieve entries for f(x) that are divisible
		// by each factor base prime.
		for j := range si {
			f := &si[j]
			pk := int(f.pk)
			lg_p := f.lg_p
			j := int(f.off)
			for ; j < window; j += pk {
				sieve[j] += lg_p
			}
			f.off = int32(j - window) // for next time
		}
		for j := 0; j < window; j++ {
			if sieve[j] >= threshold {
				r = append(r, i+j)
			}
		}
	}
	return r
}

// Find values of x for which f(x) = a x^2 + b x + c factors (within one bigprime) over the primes in fb.
// requires: a > 0
func sievesmooth(a, b, c bigint, fb []int64, rnd *rand.Rand) []sieveResult {
	var result []sieveResult

	maxp := fb[len(fb)-1]

	// find approximate zero crossings
	d := b.Square().Sub(a.Mul(c).Lsh(2))
	if d.Sign() < 0 {
		panic("polynomial has no roots")
		// TODO: choose min instead?  Then x = -b/2a
	}
	x := b.Neg().Add(d.SqrtFloor()).Div(a).Rsh(1)
	//x2 := b.Neg().Sub(d).Div(a).Rsh(1)
	// TODO: sieve around x2 also? (if d != 0)

	// starting point
	x0 := x.Sub64(sieverange)

	// results buffer
	var factors []uint

	// find starting points
	si := makeSieveInfo2(a, b, c, x0, fb, rnd)

	// pick threshold
	threshold := byte(a.Mul(x0).Add(b).Mul(x0).Add(c).BitLen()) - 2*log2(maxp) // TODO: subtract more?

	// sieve to find any potential smooth f(x)
	sieve := make([]byte, window) // TODO: cache this?
	res := sieveinner(sieve, si, threshold)

	// check potential results using trial factorization
	for _, i := range res {
		// compute f(x)
		x := x0.Add64(int64(i))
		y := a.Mul(x).Add(b).Mul(x).Add(c)

		// trial divide f by the factor base
		// accumulate factor base indexes of factors
		factors = factors[:0]
		for k, p := range fb {
			if p == -1 {
				if y.Sign() < 0 {
					y = y.Neg()
					factors = append(factors, uint(k))
				}
				continue
			}
			for y.Mod64(p) == 0 {
				y = y.Div64(p)
				factors = append(factors, uint(k))
			}
		}

		// if remainder > B^2, it's too big, might not be prime.
		if y.Cmp64(maxp*maxp) > 0 {
			//fmt.Printf("  false positive y=%d z=%d threshold=%d sieve[i]=%d log2(y)=%d log2(y/z)=%d\n", y, bigz, threshold, sieve[i], y.BitLen(), x.Div(y, bigz).BitLen())
			continue
		}

		result = append(result, sieveResult{x, dup(factors), y.Int64()})
	}
	return result
}

type sieveinfo2 struct {
	pk   int32 // p^k for this factor base entry
	lg_p uint8 // ~log_2(p)
	off  int32 // working offset in sieve array
}

func makeSieveInfo2(a, b, c bigint, start bigint, fb []int64, rnd *rand.Rand) []sieveinfo2 {
	var si []sieveinfo2

	for _, p := range fb[1:] {
		pk := p
		for k := uint(1); k < 2; k++ { // TODO: quadraticModPK
			if pk > fb[len(fb)-1] {
				// Kind of arbitrary, but use powers of p as long as p^k is
				// smaller than than the maximum factor base prime.
				break
			}
			s := start.Mod64(pk)
			for _, r := range quadraticModP(a.Mod64(pk), b.Mod64(pk), c.Mod64(pk), pk, rnd) {
				// find first pk*i+r which is >= start
				off := (r - s + pk) % pk
				si = append(si, sieveinfo2{int32(pk), log2(p), int32(off)})
			}
			pk *= p
		}
	}
	return si
}

func init() {
	factorizers["qs2"] = qs2
}

func qs2(n bigint, rnd *rand.Rand) []bigint {
	// qs does not work for powers of a single prime.  Check that first.
	if f := primepower(n, rnd); f != nil {
		return f
	}

	// first, pick a factor base
	fb, a := makeFactorBase(n)
	if a != 0 {
		return []bigint{Big(a), n.Div64(a)}
	}

	for _, r := range sievesmooth(Big(1), Big(0), n.Neg(), fb, rnd) {
		fmt.Printf("f(%d)= prod %v * %d\n", r.x, r.factors, r.remainder)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Randall Farmer. All rights reserved.

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Package index uses sorted arrays of integers to assist sorting and
// searching, particularly of collections of strings.
package index

import (
	"sort"

	"github.com/twotwotwo/sorts"
)

type Index struct {
	Keys      []uint64
	Summary   []uint64 // implicit B-tree, if Summarize() was called
	Data      sort.Interface
	// intereted in these but not ready yet
	// Offset    int // index subslice where keys[0] is data[Offset]
	// KeyOffset int // for string keys that are 8th-15th bytes not 0-7th
}

// Len returns the length of the data underlying an Index
func (idx *Index) Len() int { return idx.Data.Len() }

// Less compares Index elements by their Keys, falling back to Data.Less for
// equal-keyed items.
func (idx *Index) Less(i, j int) bool {
	return idx.Keys[i] < idx.Keys[j] || (idx.Keys[i] == idx.Keys[j] && idx.Data.Less(i, j))
}

// Swap swaps both the Keys and the inderlying data items at indices i and
// j.
func (idx *Index) Swap(i, j int) {
	idx.Keys[i], idx.Keys[j] = idx.Keys[j], idx.Keys[i]
	idx.Data.Swap(i, j)
}

// Key returns the uint64 key at index i.
func (idx *Index) Key(i int) uint64 { return idx.Keys[i] }

// levelBits and pageSize control the fan-out of Summary, the implicit
// B-tree.  6 won a very informal bake-off.  (Would have guessed 3, matching
// 8-word amd64 cache lines.) More would work better if this were ever on
// block storage, e.g.  levelBits of 9 corresponds to a page size of 4KiB.
const levelBits = 6
const pageSize = 1 << levelBits

// Summarize makes an implicit B-tree to speed lookups, using a few percent
// overhead on top of what's already used for Indices.
func (idx *Index) Summarize() {
	l := idx.Len()
	sl := l>>levelBits + l>>levelBits*2 + l>>levelBits*3 + l>>((levelBits*4)-1)
	summary := make([]uint64, 0, sl)
	summarizing := idx.Keys
	levelNum := 1
	for len(summarizing) > pageSize {
		start := len(summary)
		for i := 0; i < len(summarizing); i += pageSize {
			summary = append(summary, summarizing[i])
		}
		summarizing = summary[start:]
		levelNum++
	}
	idx.Summary = summary
}

// FindKey finds the position of the first item >= key in Keys, returning
// one after the end if there is none, and possibly using Summary for a
// speedup.  To find a range equal to k, you might use the range from
// FindKey(k) up to but not including FindKey(k+1) (with a special case
// using idx.Len() for k==^uint64(0), if relevant).  When different values
// map to the same key, you might want to sort.Search within the range to
// narrow your result down to the desired values.
func (idx *Index) FindKey(key uint64) int {
	if idx.Summary != nil {
		return idx.findKeySummary(key)
	}
	return sort.Search(idx.Len(), func(i int) bool { return idx.Keys[i] >= key })
}

// RF: awkwardness of docs above makes it mighty tempting to provide
// FindKeyRange and special cases for StringInterface and BytesInterface.

func (idx *Index) findKeySummary(key uint64) int {
	summary := idx.Summary
	keys := idx.Keys

	// count how many layers to expect in the "btree"
	levels, l := 0, len(keys)
	for l > 0 {
		levels++
		l >>= levelBits
	}
	levels-- // went one too far

	// keep following largest-strictly-less down the chain
	levelNum := levels
	levelEnd := len(summary)
	offset := 0
	for levelNum > 0 {
		// extract the "level"
		thisLevelBits := uint(levelBits * levelNum)
		levelLen := len(keys) >> thisLevelBits
		if len(keys) > levelLen<<thisLevelBits {
			// an entry for the remainder
			levelLen++
		}
		level := summary[levelEnd-levelLen : levelEnd]

		// extract the page at the given offset
		pageEnd := offset + pageSize
		if pageEnd > len(level) {
			pageEnd = len(level)
		}
		page := level[offset:pageEnd]

		// scan page for an entry >= key
		// binsearch would be fewer operations but less predictable ones
		i := 0
		for i < len(page) && page[i] < key {
			i++
		}
		if i > 0 {
			// i is first one that goes too far
			i--
		}

		// use that to walk down the tree
		// in particular, get next offset and level location
		offset += i
		offset <<= levelBits
		levelEnd -= levelLen
		levelNum--
	}

	// level==0 is the original array
	pageEnd := offset + pageSize
	if pageEnd > len(keys) {
		pageEnd = len(keys)
	}
	page := keys[offset:pageEnd]
	i := 0
	for i < len(page) && page[i] < key {
		i++
	}
	return offset + i
}

// A type satisfying KeySetter can fill a []uint64 with numeric sort keys
// for its items.  Distinct values may map to the same integer key; Less
// is used as a tiebreaker.
type KeySetter interface {
	SetKeys([]uint64)
}

// StringKey generates a uint64 key from the first bytes of key.
func StringKey(key string) uint64 {
	k := uint64(0)
	for j := 0; j < 8 && j < len(key); j++ {
		k ^= uint64(key[j]) << uint(56-8*j)
	}
	return k
}

// BytesKey generates a uint64 key from the first bytes of key.
func BytesKey(key []byte) uint64 {
	k := uint64(0)
	for j := 0; j < 8 && j < len(key); j++ {
		k ^= uint64(key[j]) << uint(56-8*j)
	}
	return k
}

// SortWithIndex allocates an Index with space for a uint64 key for each
// item in data, then sorts items by their uint64 keys, using data.Less as a
// tie-breaker for equal-keyed items.  data may implement index.KeySetter or
// any of sorts.StringInterface, BytesInterface, or Uint64Interface.
func SortWithIndex(data sort.Interface) *Index {
	l := data.Len()
	indices := make([]uint64, l)
	idx := &Index{
		Keys: indices,
		Data: data,
	}
	switch data := data.(type) {
	case KeySetter:
		data.SetKeys(idx.Keys)
	case sorts.StringInterface:
		for i := 0; i < l; i++ {
			key := data.Key(i)
			k := uint64(0)
			for j := 0; j < 8 && j < len(key); j++ {
				k ^= uint64(key[j]) << uint(56-8*j)
			}
			indices[i] = k
		}
	case sorts.BytesInterface:
		for i := 0; i < l; i++ {
			key := data.Key(i)
			k := uint64(0)
			for j := 0; j < 8 && j < len(key); j++ {
				k ^= uint64(key[j]) << uint(56-8*j)
			}
			indices[i] = k
		}
	case sorts.Uint64Interface:
		for i := 0; i < l; i++ {
			indices[i] = data.Key(i)
		}
	default:
		panic("don't know how to extract int keys for data")
	}
	sorts.ByUint64(idx)
	return idx
}
<commit_msg>FindString/FindBytes -- have only played with these from toy programs; need to test<commit_after>// Copyright 2015 Randall Farmer. All rights reserved.

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Package index uses sorted arrays of integers to assist sorting and
// searching, particularly of collections of strings.
package index

import (
	"bytes"
	"sort"
	"strings"

	"github.com/twotwotwo/sorts"
)

type Index struct {
	Keys    []uint64
	Summary []uint64 // implicit B-tree, if Summarize() was called
	Data    sort.Interface
}

// Len returns the length of the data underlying an Index
func (idx *Index) Len() int { return idx.Data.Len() }

// Less compares Index elements by their Keys, falling back to Data.Less for
// equal-keyed items.
func (idx *Index) Less(i, j int) bool {
	return idx.Keys[i] < idx.Keys[j] || (idx.Keys[i] == idx.Keys[j] && idx.Data.Less(i, j))
}

// Swap swaps both the Keys and the inderlying data items at indices i and
// j.
func (idx *Index) Swap(i, j int) {
	idx.Keys[i], idx.Keys[j] = idx.Keys[j], idx.Keys[i]
	idx.Data.Swap(i, j)
}

// Key returns the uint64 key at index i.
func (idx *Index) Key(i int) uint64 { return idx.Keys[i] }

// levelBits and pageSize control the fan-out of Summary, the implicit
// B-tree.  6 won a very informal bake-off.  (Would have guessed 3, matching
// 8-word amd64 cache lines.) More would work better if this were ever on
// block storage, e.g.  levelBits of 9 corresponds to a page size of 4KiB.
const levelBits = 6
const pageSize = 1 << levelBits

// Summarize makes an implicit B-tree to speed lookups, using a few percent
// overhead on top of what's already used for Indices.
func (idx *Index) Summarize() {
	l := idx.Len()
	sl := l>>levelBits + l>>levelBits*2 + l>>levelBits*3 + l>>((levelBits*4)-1)
	summary := make([]uint64, 0, sl)
	summarizing := idx.Keys
	levelNum := 1
	for len(summarizing) > pageSize {
		start := len(summary)
		for i := 0; i < len(summarizing); i += pageSize {
			summary = append(summary, summarizing[i])
		}
		summarizing = summary[start:]
		levelNum++
	}
	idx.Summary = summary
}

// FindUint64 finds the position of the first item >= key in Keys, returning
// one after the end if there is none.  When different values map to the same key,
// you might want to sort.Search within the returned range to narrow your result
// down to the desired values.
func (idx *Index) FindUint64(key uint64) int {
	if idx.Summary != nil {
		return idx.findUint64Summary(key)
	}
	return sort.Search(idx.Len(), func(i int) bool { return idx.Keys[i] >= key })
}

// Compares string a to []byte b, returning -1 if a<b, 0 if a==b, and 1 if a>b.
func CompareStringToBytes(a string, b []byte) int {
	for i := range b {
		if i > len(a) {
			return -1
		}
		if b[i] > a[i] {
			return -1
		}
		if a[i] > b[i] {
			return 1
		}
	}
	if len(a) > len(b) {
		return 1
	}
	return 0
}

// CompareBytesToString is a convenience wrapper for CompareStringToBytes.
func CompareBytesToString(a []byte, b string) int {
	return -CompareStringToBytes(b, a)
}

// FindString finds the first item >= key, returning one after the end if there
// is none. The collection type must implement Key(i) returning string or []byte.
func (idx *Index) FindString(key string) int {
	k := StringKey(key)
	a, b := idx.FindUint64Range(k)
	switch data := idx.Data.(type) {
	case sorts.StringInterface:
		return a + sort.Search(b-a, func(i int) bool {
			return strings.Compare(key, data.Key(a+i)) >= 0
		})
	case sorts.BytesInterface:
		return a + sort.Search(b-a, func(i int) bool {
			return CompareStringToBytes(key, data.Key(a+i)) >= 0
		})
	default:
		panic("to use FindStringKey, Data.Key(i) must return string or []byte")
	}
}

// FindBytes finds the first item >= key, returning one after the end if there
// is none. The collection type must implement Key(i) returning string or []byte.
func (idx *Index) FindBytes(key []byte) int {
	k := BytesKey(key)
	a, b := idx.FindUint64Range(k)
	switch data := idx.Data.(type) {
	case sorts.StringInterface:
		return a + sort.Search(b-a, func(i int) bool {
			return CompareBytesToString(key, data.Key(a+i)) >= 0
		})
	case sorts.BytesInterface:
		offset := sort.Search(b-a, func(i int) bool {
			return bytes.Compare(key, data.Key(a+i)) >= 0
		})
		return a + offset
	default:
		panic("to use FindBytesKey, Data.Key(i) must return string or []byte")
	}
}

// FindUint64Range looks for a range of keys such that all items in idx.Keys[a:b]
// equal key.
// It can return an empty range if the item isn't found; in that case, a and b are both where the item would be inserted (and can be one past the end).
// To find a single key, use FindUint64.
func (idx *Index) FindUint64Range(key uint64) (a, b int) {
	a = idx.FindUint64(key)
	if a == len(idx.Keys) || idx.Keys[a] != key {
		// key not found, needn't search again
		return a, a
	}
	if key == ^uint64(0) { // would overflow
		b = len(idx.Keys)
	} else {
		b = idx.FindUint64(key + 1)
	}
	return
}

// FindStringRange(key) finds the range (a,b] such that Key() returns key for all items in idx.Data[a:b].
// It can return an empty range if the item isn't found; in that case, a and b are both where the item would be inserted (and can be one past the end).
// Data must implement Key(i) returning string or []byte.
// To find a single item, use FindString.
func (idx *Index) FindStringRange(key string) (int, int) {
	k := StringKey(key)
	a, b := idx.FindUint64Range(k)
	switch data := idx.Data.(type) {
	case sorts.StringInterface:
		aa := a + sort.Search(b-a, func(i int) bool {
			return strings.Compare(key, data.Key(a+i)) >= 0
		})
		bb := aa + sort.Search(b-aa, func(i int) bool {
			return strings.Compare(key, data.Key(aa+i)) > 0
		})
		return aa, bb
	case sorts.BytesInterface:
		aa := a + sort.Search(b-a, func(i int) bool {
			return CompareStringToBytes(key, data.Key(a+i)) >= 0
		})
		bb := aa + sort.Search(b-aa, func(i int) bool {
			return CompareStringToBytes(key, data.Key(aa+i)) > 0
		})
		return aa, bb
	default:
		panic("to use FindStringRange, Data.Key(i) must return string or []byte")
	}
}

// FindBytesRange(key) finds the range (a,b] such that Key() returns key for all items in idx.Data[a:b].
// It can return an empty range if the item isn't found; in that case, a is where the item would be inserted (and can be one past the end).
// Data must implement Key(i) returning string or []byte.
// To find a single item, use FindBytes.
func (idx *Index) FindBytesRange(key []byte) (int, int) {
	k := BytesKey(key)
	a, b := idx.FindUint64Range(k)
	switch data := idx.Data.(type) {
	case sorts.StringInterface:
		aa := a + sort.Search(b-a, func(i int) bool {
			return CompareBytesToString(key, data.Key(a+i)) >= 0
		})
		bb := aa + sort.Search(b-aa, func(i int) bool {
			return CompareBytesToString(key, data.Key(aa+i)) > 0
		})
		return aa, bb
	case sorts.BytesInterface:
		aa := a + sort.Search(b-a, func(i int) bool {
			return bytes.Compare(key, data.Key(a+i)) >= 0
		})
		bb := aa + sort.Search(b-aa, func(i int) bool {
			return bytes.Compare(key, data.Key(aa+i)) > 0
		})
		return aa, bb
	default:
		panic("to use FindStringRangeExact, Data.Key(i) must return string or []byte")
	}
}

func (idx *Index) findUint64Summary(key uint64) int {
	summary := idx.Summary
	keys := idx.Keys

	// count how many layers to expect in the "btree"
	levels, l := 0, len(keys)
	for l > 0 {
		levels++
		l >>= levelBits
	}
	levels-- // went one too far

	// keep following largest-strictly-less down the chain
	levelNum := levels
	levelEnd := len(summary)
	offset := 0
	for levelNum > 0 {
		// extract the "level"
		thisLevelBits := uint(levelBits * levelNum)
		levelLen := len(keys) >> thisLevelBits
		if len(keys) > levelLen<<thisLevelBits {
			// an entry for the remainder
			levelLen++
		}
		level := summary[levelEnd-levelLen : levelEnd]

		// extract the page at the given offset
		pageEnd := offset + pageSize
		if pageEnd > len(level) {
			pageEnd = len(level)
		}
		page := level[offset:pageEnd]

		// scan page for an entry >= key
		// binsearch would be fewer operations but less predictable ones
		i := 0
		for i < len(page) && page[i] < key {
			i++
		}
		if i > 0 {
			// i is first one that goes too far
			i--
		}

		// use that to walk down the tree
		// in particular, get next offset and level location
		offset += i
		offset <<= levelBits
		levelEnd -= levelLen
		levelNum--
	}

	// level==0 is the original array
	pageEnd := offset + pageSize
	if pageEnd > len(keys) {
		pageEnd = len(keys)
	}
	page := keys[offset:pageEnd]
	i := 0
	for i < len(page) && page[i] < key {
		i++
	}
	return offset + i
}

// StringKey generates a uint64 key from the first bytes of key.
func StringKey(key string) uint64 {
	k := uint64(0)
	for j := 0; j < 8 && j < len(key); j++ {
		k ^= uint64(key[j]) << uint(56-8*j)
	}
	return k
}

// BytesKey generates a uint64 key from the first bytes of key.
func BytesKey(key []byte) uint64 {
	k := uint64(0)
	for j := 0; j < 8 && j < len(key); j++ {
		k ^= uint64(key[j]) << uint(56-8*j)
	}
	return k
}

// SortWithIndex allocates an Index with space for a uint64 key for each
// item in data, then sorts items by their uint64 keys, using data.Less as a
// tie-breaker for equal-keyed items.  data may implement index.KeySetter or
// any of sorts.StringInterface, BytesInterface, or Uint64Interface.
func SortWithIndex(data sort.Interface) *Index {
	l := data.Len()
	indices := make([]uint64, l)
	idx := &Index{
		Keys: indices,
		Data: data,
	}
	switch data := data.(type) {
	case sorts.StringInterface:
		for i := 0; i < l; i++ {
			key := data.Key(i)
			k := uint64(0)
			for j := 0; j < 8 && j < len(key); j++ {
				k ^= uint64(key[j]) << uint(56-8*j)
			}
			indices[i] = k
		}
	case sorts.BytesInterface:
		for i := 0; i < l; i++ {
			key := data.Key(i)
			k := uint64(0)
			for j := 0; j < 8 && j < len(key); j++ {
				k ^= uint64(key[j]) << uint(56-8*j)
			}
			indices[i] = k
		}
	case sorts.Uint64Interface:
		for i := 0; i < l; i++ {
			indices[i] = data.Key(i)
		}
	default:
		panic("don't know how to extract int keys for data")
	}
	sorts.ByUint64(idx)
	return idx
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/system"
	"os"
	"path/filepath"
	"syscall"
)

// default mount point flags
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

// setupNewMountNamespace is used to initialize a new mount namespace for an new
// container in the rootfs that is specified.
//
// There is no need to unmount the new mounts because as soon as the mount namespace
// is no longer in use, the mounts will be removed automatically
func setupNewMountNamespace(rootfs, console string, readonly bool) error {
	// mount as slave so that the new mounts do not propagate to the host
	if err := system.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mounting / as slave %s", err)
	}
	if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mouting %s as bind %s", rootfs, err)
	}
	if readonly {
		if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
			return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
		}
	}
	if err := mountSystem(rootfs); err != nil {
		return fmt.Errorf("mount system %s", err)
	}
	if err := copyDevNodes(rootfs); err != nil {
		return fmt.Errorf("copy dev nodes %s", err)
	}
	if err := setupDev(rootfs); err != nil {
		return err
	}
	if err := setupPtmx(rootfs, console); err != nil {
		return err
	}
	if err := system.Chdir(rootfs); err != nil {
		return fmt.Errorf("chdir into %s %s", rootfs, err)
	}
	if err := system.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
		return fmt.Errorf("mount move %s into / %s", rootfs, err)
	}
	if err := system.Chroot("."); err != nil {
		return fmt.Errorf("chroot . %s", err)
	}
	if err := system.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}

	system.Umask(0022)

	return nil
}

// copyDevNodes mknods the hosts devices so the new container has access to them
func copyDevNodes(rootfs string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	for _, node := range []string{
		"null",
		"zero",
		"full",
		"random",
		"urandom",
		"tty",
	} {
		stat, err := os.Stat(filepath.Join("/dev", node))
		if err != nil {
			return err
		}
		var (
			dest = filepath.Join(rootfs, "dev", node)
			st   = stat.Sys().(*syscall.Stat_t)
		)
		if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
			return fmt.Errorf("copy %s %s", node, err)
		}
	}
	return nil
}

// setupDev symlinks the current processes pipes into the
// appropriate destination on the containers rootfs
func setupDev(rootfs string) error {
	for _, link := range []struct {
		from string
		to   string
	}{
		{"/proc/kcore", "/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	} {
		dest := filepath.Join(rootfs, link.to)
		if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove %s %s", dest, err)
		}
		if err := os.Symlink(link.from, dest); err != nil {
			return fmt.Errorf("symlink %s %s", dest, err)
		}
	}
	return nil
}

// setupConsole ensures that the container has a proper /dev/console setup
func setupConsole(rootfs, console string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	stat, err := os.Stat(console)
	if err != nil {
		return fmt.Errorf("stat console %s %s", console, err)
	}
	var (
		st   = stat.Sys().(*syscall.Stat_t)
		dest = filepath.Join(rootfs, "dev/console")
	)
	if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove %s %s", dest, err)
	}
	if err := os.Chmod(console, 0600); err != nil {
		return err
	}
	if err := os.Chown(console, 0, 0); err != nil {
		return err
	}
	if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
		return fmt.Errorf("mknod %s %s", dest, err)
	}
	if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
		return fmt.Errorf("bind %s to %s %s", console, dest, err)
	}
	return nil
}

// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
// inside the mount namespace
func mountSystem(rootfs string) error {
	for _, m := range []struct {
		source string
		path   string
		device string
		flags  int
		data   string
	}{
		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},
		{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: "mode=755"},
		{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: "mode=1777"},
		{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
		{source: "tmpfs", path: filepath.Join(rootfs, "run"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, data: "mode=755"},
	} {
		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
			return fmt.Errorf("mkdirall %s %s", m.path, err)
		}
		if err := system.Mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
			return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
		}
	}
	return nil
}

// setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and
// finishes setting up /dev/console
func setupPtmx(rootfs, console string) error {
	ptmx := filepath.Join(rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if err := setupConsole(rootfs, console); err != nil {
		return err
	}
	return nil
}

// remountProc is used to detach and remount the proc filesystem
// commonly needed with running a new process inside an existing container
func remountProc() error {
	if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil {
		return err
	}
	if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil {
		return err
	}
	return nil
}

func remountSys() error {
	if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil {
		if err != syscall.EINVAL {
			return err
		}
	} else {
		if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>WIP for setup kmsg Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)<commit_after>// +build linux

package main

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/system"
	"os"
	"path/filepath"
	"syscall"
)

// default mount point flags
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

// setupNewMountNamespace is used to initialize a new mount namespace for an new
// container in the rootfs that is specified.
//
// There is no need to unmount the new mounts because as soon as the mount namespace
// is no longer in use, the mounts will be removed automatically
func setupNewMountNamespace(rootfs, console string, readonly bool) error {
	// mount as slave so that the new mounts do not propagate to the host
	if err := system.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mounting / as slave %s", err)
	}
	if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mouting %s as bind %s", rootfs, err)
	}
	if readonly {
		if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
			return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
		}
	}
	if err := mountSystem(rootfs); err != nil {
		return fmt.Errorf("mount system %s", err)
	}
	if err := copyDevNodes(rootfs); err != nil {
		return fmt.Errorf("copy dev nodes %s", err)
	}
	if err := setupDev(rootfs); err != nil {
		return err
	}
	if err := setupPtmx(rootfs, console); err != nil {
		return err
	}
	if err := setupKmsg(rootfs); err != nil {
		return err
	}
	if err := system.Chdir(rootfs); err != nil {
		return fmt.Errorf("chdir into %s %s", rootfs, err)
	}
	if err := system.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
		return fmt.Errorf("mount move %s into / %s", rootfs, err)
	}
	if err := system.Chroot("."); err != nil {
		return fmt.Errorf("chroot . %s", err)
	}
	if err := system.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}

	system.Umask(0022)

	return nil
}

// copyDevNodes mknods the hosts devices so the new container has access to them
func copyDevNodes(rootfs string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	for _, node := range []string{
		"null",
		"zero",
		"full",
		"random",
		"urandom",
		"tty",
	} {
		stat, err := os.Stat(filepath.Join("/dev", node))
		if err != nil {
			return err
		}
		var (
			dest = filepath.Join(rootfs, "dev", node)
			st   = stat.Sys().(*syscall.Stat_t)
		)
		if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
			return fmt.Errorf("copy %s %s", node, err)
		}
	}
	return nil
}

// setupDev symlinks the current processes pipes into the
// appropriate destination on the containers rootfs
func setupDev(rootfs string) error {
	for _, link := range []struct {
		from string
		to   string
	}{
		{"/proc/kcore", "/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	} {
		dest := filepath.Join(rootfs, link.to)
		if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove %s %s", dest, err)
		}
		if err := os.Symlink(link.from, dest); err != nil {
			return fmt.Errorf("symlink %s %s", dest, err)
		}
	}
	return nil
}

// setupConsole ensures that the container has a proper /dev/console setup
func setupConsole(rootfs, console string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	stat, err := os.Stat(console)
	if err != nil {
		return fmt.Errorf("stat console %s %s", console, err)
	}
	var (
		st   = stat.Sys().(*syscall.Stat_t)
		dest = filepath.Join(rootfs, "dev/console")
	)
	if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove %s %s", dest, err)
	}
	if err := os.Chmod(console, 0600); err != nil {
		return err
	}
	if err := os.Chown(console, 0, 0); err != nil {
		return err
	}
	if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
		return fmt.Errorf("mknod %s %s", dest, err)
	}
	if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
		return fmt.Errorf("bind %s to %s %s", console, dest, err)
	}
	return nil
}

// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
// inside the mount namespace
func mountSystem(rootfs string) error {
	for _, m := range []struct {
		source string
		path   string
		device string
		flags  int
		data   string
	}{
		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},
		{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: "mode=755"},
		{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: "mode=1777"},
		{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
		{source: "tmpfs", path: filepath.Join(rootfs, "run"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, data: "mode=755"},
	} {
		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
			return fmt.Errorf("mkdirall %s %s", m.path, err)
		}
		if err := system.Mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
			return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
		}
	}
	return nil
}

// setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and
// finishes setting up /dev/console
func setupPtmx(rootfs, console string) error {
	ptmx := filepath.Join(rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if err := setupConsole(rootfs, console); err != nil {
		return err
	}
	return nil
}

// remountProc is used to detach and remount the proc filesystem
// commonly needed with running a new process inside an existing container
func remountProc() error {
	if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil {
		return err
	}
	if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil {
		return err
	}
	return nil
}

func remountSys() error {
	if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil {
		if err != syscall.EINVAL {
			return err
		}
	} else {
		if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil {
			return err
		}
	}
	return nil
}

func setupKmsg(rootfs string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	var (
		source = filepath.Join(rootfs, "dev/kmsg")
		dest   = filepath.Join(rootfs, "proc/kmsg")
	)

	if err := system.Mkfifo(source, 0600); err != nil {
		return err
	}

	os.Chmod(source, 0600)
	os.Chown(source, 0, 0)

	if err := system.Mount(source, dest, "bind", syscall.MS_BIND, ""); err != nil {
		return err
	}
	_, err := os.OpenFile(source, syscall.O_RDWR|syscall.O_NDELAY|syscall.O_CLOEXEC, 0)
	if err != nil {
		return err
	}
	if err := syscall.Unlink(source); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package geo_test

import (
	"fmt"
	"os"

	"github.com/codingsince1985/geo-golang"
	"github.com/codingsince1985/geo-golang/bing"
	"github.com/codingsince1985/geo-golang/chained"
	"github.com/codingsince1985/geo-golang/google"
	"github.com/codingsince1985/geo-golang/here"
	"github.com/codingsince1985/geo-golang/mapbox"
	"github.com/codingsince1985/geo-golang/mapquest/nominatim"
	"github.com/codingsince1985/geo-golang/mapquest/open"
	"github.com/codingsince1985/geo-golang/opencage"
	"github.com/codingsince1985/geo-golang/openstreetmap"
)

const addr = "Melbourne VIC"
const lat, lng = -37.8167416, 144.964463
const RADIUS = 50

func ExampleGeocoder() {
	fmt.Println("Google Geocoding API")
	try(google.Geocoder(os.Getenv("GOOGLE_API_KEY")))

	fmt.Println("Mapquest Nominatim")
	try(nominatim.Geocoder(os.Getenv("MAPQUEST_NOMINATUM_KEY")))

	fmt.Println("Mapquest Open streetmaps")
	try(open.Geocoder(os.Getenv("MAPQUEST_OPEN_KEY")))

	fmt.Println("OpenCage Data")
	try(opencage.Geocoder(os.Getenv("OPENCAGE_API_KEY")))

	fmt.Println("HERE API")
	try(here.Geocoder(os.Getenv("HERE_APP_ID"), os.Getenv("HERE_APP_CODE"), RADIUS))

	fmt.Println("Bing Geocoding API")
	try(bing.Geocoder(os.Getenv("BING_API_KEY")))

	fmt.Println("Mapbox API")
	try(mapbox.Geocoder(os.Getenv("MAPBOX_API_KEY")))

	fmt.Println("OpenStreetMap")
	try(openstreetmap.Geocoder())

	// Chained geocoder will fallback to subsequent geocoders
	fmt.Println("ChainedAPI[OpenStreetmap -> Google]")
	try(chained.Geocoder(
		openstreetmap.Geocoder(),
		google.Geocoder(os.Getenv("GOOGLE_API_KEY")),
	))
	// Output: Google Geocoding API
	// Melbourne VIC location is (-37.814107, 144.963280)
	// Address of (-37.816742,144.964463) is 66 Elizabeth St, Melbourne VIC 3000, Australia
	//
	// Mapquest Nominatim
	// Melbourne VIC location is (-37.814218, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
	//
	// Mapquest Open streetmaps
	// Melbourne VIC location is (-37.814218, 144.963161)
	// Address of (-37.816742,144.964463) is Elizabeth Street, Melbourne, Victoria, AU
	//
	// OpenCage Data
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne VIC 3000, Australia
	//
	// HERE API
	// Melbourne VIC location is (-37.817530, 144.967150)
	// Address of (-37.816742,144.964463) is 40 Elizabeth St, Melbourne VIC 3000, Australia
	//
	// Bing Geocoding API
	// Melbourne VIC location is (-37.824299, 144.977997)
	// Address of (-37.816742,144.964463) is 46 Elizabeth St, Melbourne, VIC 3000
	//
	// Mapbox API
	// Melbourne VIC location is (-37.814200, 144.963200)
	// Address of (-37.816742,144.964463) is Clegs, 60 Elizabeth St, 3000 Melbourne, Australia
	//
	// OpenStreetMap
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
	//
	// ChainedAPI[OpenStreetmap -> Google]
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
}

func try(geocoder geo.Geocoder) {
	location, _ := geocoder.Geocode(addr)
	fmt.Printf("%s location is (%.6f, %.6f)\n", addr, location.Lat, location.Lng)
	address, _ := geocoder.ReverseGeocode(lat, lng)
	fmt.Printf("Address of (%.6f,%.6f) is %s\n\n", lat, lng, address)
}
<commit_msg>Google changes Mel location<commit_after>package geo_test

import (
	"fmt"
	"os"

	"github.com/codingsince1985/geo-golang"
	"github.com/codingsince1985/geo-golang/bing"
	"github.com/codingsince1985/geo-golang/chained"
	"github.com/codingsince1985/geo-golang/google"
	"github.com/codingsince1985/geo-golang/here"
	"github.com/codingsince1985/geo-golang/mapbox"
	"github.com/codingsince1985/geo-golang/mapquest/nominatim"
	"github.com/codingsince1985/geo-golang/mapquest/open"
	"github.com/codingsince1985/geo-golang/opencage"
	"github.com/codingsince1985/geo-golang/openstreetmap"
)

const addr = "Melbourne VIC"
const lat, lng = -37.8167416, 144.964463
const RADIUS = 50

func ExampleGeocoder() {
	fmt.Println("Google Geocoding API")
	try(google.Geocoder(os.Getenv("GOOGLE_API_KEY")))

	fmt.Println("Mapquest Nominatim")
	try(nominatim.Geocoder(os.Getenv("MAPQUEST_NOMINATUM_KEY")))

	fmt.Println("Mapquest Open streetmaps")
	try(open.Geocoder(os.Getenv("MAPQUEST_OPEN_KEY")))

	fmt.Println("OpenCage Data")
	try(opencage.Geocoder(os.Getenv("OPENCAGE_API_KEY")))

	fmt.Println("HERE API")
	try(here.Geocoder(os.Getenv("HERE_APP_ID"), os.Getenv("HERE_APP_CODE"), RADIUS))

	fmt.Println("Bing Geocoding API")
	try(bing.Geocoder(os.Getenv("BING_API_KEY")))

	fmt.Println("Mapbox API")
	try(mapbox.Geocoder(os.Getenv("MAPBOX_API_KEY")))

	fmt.Println("OpenStreetMap")
	try(openstreetmap.Geocoder())

	// Chained geocoder will fallback to subsequent geocoders
	fmt.Println("ChainedAPI[OpenStreetmap -> Google]")
	try(chained.Geocoder(
		openstreetmap.Geocoder(),
		google.Geocoder(os.Getenv("GOOGLE_API_KEY")),
	))
	// Output: Google Geocoding API
	// Melbourne VIC location is (-37.816279, 144.964246)
	// Address of (-37.816742,144.964463) is 40 Elizabeth St, Melbourne VIC 3004, Australia
	//
	// Mapquest Nominatim
	// Melbourne VIC location is (-37.814218, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
	//
	// Mapquest Open streetmaps
	// Melbourne VIC location is (-37.814218, 144.963161)
	// Address of (-37.816742,144.964463) is Elizabeth Street, Melbourne, Victoria, AU
	//
	// OpenCage Data
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne VIC 3000, Australia
	//
	// HERE API
	// Melbourne VIC location is (-37.817530, 144.967150)
	// Address of (-37.816742,144.964463) is 40 Elizabeth St, Melbourne VIC 3000, Australia
	//
	// Bing Geocoding API
	// Melbourne VIC location is (-37.824299, 144.977997)
	// Address of (-37.816742,144.964463) is 46 Elizabeth St, Melbourne, VIC 3000
	//
	// Mapbox API
	// Melbourne VIC location is (-37.814200, 144.963200)
	// Address of (-37.816742,144.964463) is Clegs, 60 Elizabeth St, 3000 Melbourne, Australia
	//
	// OpenStreetMap
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
	//
	// ChainedAPI[OpenStreetmap -> Google]
	// Melbourne VIC location is (-37.814217, 144.963161)
	// Address of (-37.816742,144.964463) is Bankwest, Elizabeth Street, Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia
}

func try(geocoder geo.Geocoder) {
	location, _ := geocoder.Geocode(addr)
	fmt.Printf("%s location is (%.6f, %.6f)\n", addr, location.Lat, location.Lng)
	address, _ := geocoder.ReverseGeocode(lat, lng)
	fmt.Printf("Address of (%.6f,%.6f) is %s\n\n", lat, lng, address)
}
<|endoftext|>


<commit_before>// Copyright 2014, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package numerus

import "testing"

// TestDescNumerals is a sanity check for the internal array descNumerals
// which must be strictly decreasing and contain non-zero values
func TestDescNumerals(t *testing.T) {
	last := uint(0)
	for i, v := range descNumerals {
		if v.Value() == 0 {
			t.Errorf("invalid (zero) value in descNumerals: %v", v)
		}
		if i != 0 {
			if v.Value() >= last {
				t.Errorf("incorrect ordering in descNumerals: %v", v)
			}
		}
		last = v.Value()
	}
}

var testPairs = []struct {
	v uint
	s string
}{
	{0, ""},
	{1, "I"},
	{2, "II"},
	{3, "III"},
	{4, "IV"},
	{5, "V"},
	{6, "VI"},
	{7, "VII"},
	{8, "VIII"},
	{9, "IX"},
	{10, "X"},
	{19, "XIX"},
	{21, "XXI"},
	{1944, "MCMXLIV"},
	{1963, "MCMLXIII"},
	{1900, "MCM"},
	{2000, "MM"},
	{3999, "MMMCMXCIX"},
}

func TestStringMethod(t *testing.T) {
	for _, tm := range testPairs {
		got := Numeral(tm.v).String()
		if got != tm.s {
			t.Errorf("Numeral(%d).String() = %v, expected %v", tm.v, got, tm.s)
		}
	}
}

func TestParseMethod(t *testing.T) {
	for _, tm := range testPairs {
		t.Run(tm.s, func(t *testing.T) {
			got, err := Parse(tm.s)
			if err != nil {
				t.Fatalf("parse(%v) gave error error: %v", tm.s, err)
			}
			if got.Value() != tm.v {
				t.Errorf("parse(%v) = %d, expected: %d", tm.s, got, tm.v)
			}
		})
	}
}

func BenchmarkParseMethod(b *testing.B) {
	for _, tm := range testPairs {
		b.Run(tm.s, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				Parse(tm.s)
			}
		})
	}
}

func BenchmarkStringMethod(b *testing.B) {
	for _, tm := range testPairs {
		b.Run(tm.s, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = Numeral(tm.v).String()
			}
		})
	}
}

var invalidInputs = []string{
	"IIII",
	"VV",
	"XXXX",
	"LL",
	"CCCC",
	"DD",
	"MMMM",
	"VIV",
	"LXL",
	"DCD",
	"MCMD",
	"IM",
	"VC",
}

func TestInvalidInputs(t *testing.T) {
	for _, in := range invalidInputs {
		if _, err := Parse(in); err == nil {
			t.Errorf("Parse(%v) should have returned an error", in)
		}
	}
}

func TestConsistency(t *testing.T) {
	i := uint(0)
	max := Limit.Value()
	for ; i < max; i++ {
		s := Numeral(i).String()
		x, err := parse(s)
		if err != nil {
			t.Errorf("Numeral(%d).String() = %v, parse(%v) gave error: %v", i, s, s, err)
		}
		if x != i {
			t.Errorf("Numeral(%d).String() = %v, parse(%v) = %d, expected: %d", i, s, s, x, i)
		}
	}
}
<commit_msg>Add examples for Parse and Numeral.String()<commit_after>// Copyright 2014, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package numerus

import (
	"fmt"
	"testing"
)

// TestDescNumerals is a sanity check for the internal array descNumerals
// which must be strictly decreasing and contain non-zero values
func TestDescNumerals(t *testing.T) {
	last := uint(0)
	for i, v := range descNumerals {
		if v.Value() == 0 {
			t.Errorf("invalid (zero) value in descNumerals: %v", v)
		}
		if i != 0 {
			if v.Value() >= last {
				t.Errorf("incorrect ordering in descNumerals: %v", v)
			}
		}
		last = v.Value()
	}
}

var testPairs = []struct {
	v uint
	s string
}{
	{0, ""},
	{1, "I"},
	{2, "II"},
	{3, "III"},
	{4, "IV"},
	{5, "V"},
	{6, "VI"},
	{7, "VII"},
	{8, "VIII"},
	{9, "IX"},
	{10, "X"},
	{19, "XIX"},
	{21, "XXI"},
	{1944, "MCMXLIV"},
	{1963, "MCMLXIII"},
	{1900, "MCM"},
	{2000, "MM"},
	{3999, "MMMCMXCIX"},
}

func TestStringMethod(t *testing.T) {
	for _, tm := range testPairs {
		got := Numeral(tm.v).String()
		if got != tm.s {
			t.Errorf("Numeral(%d).String() = %v, expected %v", tm.v, got, tm.s)
		}
	}
}

func TestParseMethod(t *testing.T) {
	for _, tm := range testPairs {
		t.Run(tm.s, func(t *testing.T) {
			got, err := Parse(tm.s)
			if err != nil {
				t.Fatalf("parse(%v) gave error error: %v", tm.s, err)
			}
			if got.Value() != tm.v {
				t.Errorf("parse(%v) = %d, expected: %d", tm.s, got, tm.v)
			}
		})
	}
}

func BenchmarkParseMethod(b *testing.B) {
	for _, tm := range testPairs {
		b.Run(tm.s, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				Parse(tm.s)
			}
		})
	}
}

func BenchmarkStringMethod(b *testing.B) {
	for _, tm := range testPairs {
		b.Run(tm.s, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = Numeral(tm.v).String()
			}
		})
	}
}

var invalidInputs = []string{
	"IIII",
	"VV",
	"XXXX",
	"LL",
	"CCCC",
	"DD",
	"MMMM",
	"VIV",
	"LXL",
	"DCD",
	"MCMD",
	"IM",
	"VC",
}

func TestInvalidInputs(t *testing.T) {
	for _, in := range invalidInputs {
		if _, err := Parse(in); err == nil {
			t.Errorf("Parse(%v) should have returned an error", in)
		}
	}
}

func TestConsistency(t *testing.T) {
	i := uint(0)
	max := Limit.Value()
	for ; i < max; i++ {
		s := Numeral(i).String()
		x, err := parse(s)
		if err != nil {
			t.Errorf("Numeral(%d).String() = %v, parse(%v) gave error: %v", i, s, s, err)
		}
		if x != i {
			t.Errorf("Numeral(%d).String() = %v, parse(%v) = %d, expected: %d", i, s, s, x, i)
		}
	}
}

func ExampleParse() {
	n, err := Parse("IV")
	if err != nil {
		fmt.Printf("Could not parse: %v", err)
	}
	fmt.Println(uint(n))
	// Output: 4
}

func ExampleNumeral_String() {
	n := Numeral(4)
	fmt.Println(n)
	// Output: IV
}
<|endoftext|>


<commit_before>package clt

import (
	"bufio"
	"fmt"
	"io"

	"os"
	"strings"

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

type getOpt int

const (
	noColon getOpt = iota
)

// InteractiveSession creates a system for collecting user input
// in response to questions and choices
type InteractiveSession struct {
	Prompt  string
	Default string
	ValHint string

	response string
	input    *bufio.Reader
	output   io.Writer
}

// NewInteractiveSession returns a new InteractiveSession outputting to Stdout
// and reading from Stdin
func NewInteractiveSession() *InteractiveSession {
	return &InteractiveSession{
		input:  bufio.NewReader(os.Stdin),
		output: os.Stdout,
	}
}

// Say is a short form of fmt.Fprintf but allows you to chain additional terminators to
// the interactive session to collect user input
func (i *InteractiveSession) Say(format string, args ...interface{}) *InteractiveSession {
	fmt.Fprintf(i.output, fmt.Sprintf("\n%s\n", format), args...)
	return i
}

// Pause is a terminator that will render long-form text added via the another method
// that returns *InteractiveSession and will wait for the user to press enter to continue.
// It is useful for long-form content or paging.
func (i *InteractiveSession) Pause() {
	i.Prompt = "\nPress [Enter] to continue."
	i.get(noColon)
}

// PauseWithPrompt is a terminator that will render long-form text added via the another method
// that returns *InteractiveSession and will wait for the user to press enter to continue.
// This will use the custom prompt specified by format and args.
func (i *InteractiveSession) PauseWithPrompt(format string, args ...interface{}) {
	i.Prompt = fmt.Sprintf(format, args...)
	i.get(noColon)
}

func (i *InteractiveSession) get(opts ...getOpt) (err error) {
	contains := func(wanted getOpt) bool {
		for _, opt := range opts {
			if opt == wanted {
				return true
			}
		}
		return false
	}

	if i.output == nil {
		i.output = bufio.NewWriter(os.Stdout)
	}
	if i.input == nil {
		i.input = bufio.NewReader(os.Stdin)
	}

	switch {
	case len(i.Default) > 0:
		fmt.Fprintf(i.output, "%s  [%s]: ", i.Prompt, i.Default)
	case len(i.ValHint) > 0:
		fmt.Fprintf(i.output, "%s (%s): ", i.Prompt, i.ValHint)
	case contains(noColon):
		fmt.Fprintf(i.output, "%s", i.Prompt)
	case len(i.Prompt) > 0:
		fmt.Fprintf(i.output, "%s: ", i.Prompt)
	default:
	}

	i.response, err = i.input.ReadString('\n')
	if err != nil {
		return err
	}
	i.response = strings.TrimRight(i.response, " \n")
	if len(i.Default) > 0 && len(i.response) == 0 {
		switch i.Default {
		case "y/N":
			i.response = "n"
		case "Y/n":
			i.response = "y"
		default:
			i.response = i.Default
		}
	}

	return nil
}

// Warn adds an informational warning message to the user in format
// Warning: <user defined string>
func (i *InteractiveSession) Warn(format string, args ...interface{}) *InteractiveSession {
	fmt.Fprintf(i.output, "\n%s: %s\n", Styled(Yellow).ApplyTo("Warning"), fmt.Sprintf(format, args...))
	return i
}

// Error is a terminator that gives an informational error message to the user in format
// Error: <user defined string>.  Exits the program returning status code 1
func (i *InteractiveSession) Error(format string, args ...interface{}) {
	fmt.Fprintf(i.output, "\n\n%s: %s\n", Styled(Red).ApplyTo("Error:"), fmt.Sprintf(format, args...))
	os.Exit(1)
}

// Ask is a terminator for an interactive session that results in returning the user
// input.  Validators can optionally be applied to ensure that acceptable input is returned
// or the question will be asked again.
func (i *InteractiveSession) Ask(prompt string, validators ...ValidationFunc) string {
	return i.ask(prompt, "", "", validators...)
}

func (i *InteractiveSession) AskWithDefault(prompt string, defaultChoice string, validators ...ValidationFunc) string {
	return i.ask(prompt, defaultChoice, "", validators...)
}

func (i *InteractiveSession) AskWithHint(prompt string, hint string, validators ...ValidationFunc) string {
	return i.ask(prompt, "", hint, validators...)
}

func (i *InteractiveSession) ask(prompt string, def string, hint string, validators ...ValidationFunc) string {
	i.Prompt = prompt
	i.Default = def
	i.ValHint = hint
	i.get()
	for _, validator := range validators {
		if ok, err := validator(i.response); !ok {
			i.Say("\nError: %s\n\n", err)
			i.ask(prompt, def, hint, validators...)
		}
	}
	return i.response
}

// AskPassword is a terminator that asks for a password and does not echo input
// to the terminal.
func (i *InteractiveSession) AskPassword(validators ...ValidationFunc) string {
	rw := bufio.NewReadWriter(i.input, bufio.NewWriter(i.output))
	term := terminal.NewTerminal(rw, "")
	pw, err := term.ReadPassword("Password: ")
	if err != nil {
		i.Error("\n%s\n\n", err)
	}
	for _, validator := range validators {
		if ok, err := validator(strings.TrimSpace(pw)); !ok {
			i.Say("\nError: %s\n\n", err)
			i.AskPassword(validators...)
		}
	}
	return strings.TrimSpace(pw)
}

func (i *InteractiveSession) AskYesNo(prompt string, defaultChoice string) string {
	switch def := strings.ToLower(defaultChoice); def {
	case "y", "yes":
		i.Default = "Y/n"
	default:
		i.Default = "y/N"
	}
	return i.ask(prompt, i.Default, "", ValidateYesNo())
}

func (i *InteractiveSession) AskFromTable(prompt string, choices map[string]string, def string) string {
	t := NewTable(2).
		ColumnHeaders("Option", "")
	var allKeys []string
	for key, choice := range choices {
		t.AddRow(key, choice)
		allKeys = append(allKeys, key)
	}
	tAsString := t.AsString()

	i.Prompt = fmt.Sprintf("\n%s%s\nChoice", prompt, tAsString)
	i.Default = def
	i.get()
	if ok, err := AllowedOptions(allKeys)(i.response); !ok {
		i.Say("\nError: %s\n\n", err)
		i.AskFromTable(prompt, choices, def)
	}

	return strings.TrimSpace(i.response)
}
<commit_msg>update godoc<commit_after>package clt

import (
	"bufio"
	"fmt"
	"io"

	"os"
	"strings"

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

type getOpt int

const (
	noColon getOpt = iota
)

// InteractiveSession creates a system for collecting user input
// in response to questions and choices
type InteractiveSession struct {
	Prompt  string
	Default string
	ValHint string

	response string
	input    *bufio.Reader
	output   io.Writer
}

// NewInteractiveSession returns a new InteractiveSession outputting to Stdout
// and reading from Stdin
func NewInteractiveSession() *InteractiveSession {
	return &InteractiveSession{
		input:  bufio.NewReader(os.Stdin),
		output: os.Stdout,
	}
}

// Say is a short form of fmt.Fprintf but allows you to chain additional terminators to
// the interactive session to collect user input
func (i *InteractiveSession) Say(format string, args ...interface{}) *InteractiveSession {
	fmt.Fprintf(i.output, fmt.Sprintf("\n%s\n", format), args...)
	return i
}

// Pause is a terminator that will render long-form text added via the another method
// that returns *InteractiveSession and will wait for the user to press enter to continue.
// It is useful for long-form content or paging.
func (i *InteractiveSession) Pause() {
	i.Prompt = "\nPress [Enter] to continue."
	i.get(noColon)
}

// PauseWithPrompt is a terminator that will render long-form text added via the another method
// that returns *InteractiveSession and will wait for the user to press enter to continue.
// This will use the custom prompt specified by format and args.
func (i *InteractiveSession) PauseWithPrompt(format string, args ...interface{}) {
	i.Prompt = fmt.Sprintf(format, args...)
	i.get(noColon)
}

func (i *InteractiveSession) get(opts ...getOpt) (err error) {
	contains := func(wanted getOpt) bool {
		for _, opt := range opts {
			if opt == wanted {
				return true
			}
		}
		return false
	}

	if i.output == nil {
		i.output = bufio.NewWriter(os.Stdout)
	}
	if i.input == nil {
		i.input = bufio.NewReader(os.Stdin)
	}

	switch {
	case len(i.Default) > 0:
		fmt.Fprintf(i.output, "%s  [%s]: ", i.Prompt, i.Default)
	case len(i.ValHint) > 0:
		fmt.Fprintf(i.output, "%s (%s): ", i.Prompt, i.ValHint)
	case contains(noColon):
		fmt.Fprintf(i.output, "%s", i.Prompt)
	case len(i.Prompt) > 0:
		fmt.Fprintf(i.output, "%s: ", i.Prompt)
	default:
	}

	i.response, err = i.input.ReadString('\n')
	if err != nil {
		return err
	}
	i.response = strings.TrimRight(i.response, " \n")
	if len(i.Default) > 0 && len(i.response) == 0 {
		switch i.Default {
		case "y/N":
			i.response = "n"
		case "Y/n":
			i.response = "y"
		default:
			i.response = i.Default
		}
	}

	return nil
}

// Warn adds an informational warning message to the user in format
// Warning: <user defined string>
func (i *InteractiveSession) Warn(format string, args ...interface{}) *InteractiveSession {
	fmt.Fprintf(i.output, "\n%s: %s\n", Styled(Yellow).ApplyTo("Warning"), fmt.Sprintf(format, args...))
	return i
}

// Error is a terminator that gives an informational error message to the user in format
// Error: <user defined string>.  Exits the program returning status code 1
func (i *InteractiveSession) Error(format string, args ...interface{}) {
	fmt.Fprintf(i.output, "\n\n%s: %s\n", Styled(Red).ApplyTo("Error:"), fmt.Sprintf(format, args...))
	os.Exit(1)
}

// Ask is a terminator for an interactive session that results in returning the user's
// input.  Validators can optionally be applied to ensure that acceptable input is returned
// or the question will be asked again.
func (i *InteractiveSession) Ask(prompt string, validators ...ValidationFunc) string {
	return i.ask(prompt, "", "", validators...)
}

// AskWithDefault is like ask, but sets a default choice that the user can select by pressing enter.
func (i *InteractiveSession) AskWithDefault(prompt string, defaultChoice string, validators ...ValidationFunc) string {
	return i.ask(prompt, defaultChoice, "", validators...)
}

// AskWithHint is like ask, but gives a hint about the proper format of the response.  This is useful
// combined with a validation function to get input in the right format.
func (i *InteractiveSession) AskWithHint(prompt string, hint string, validators ...ValidationFunc) string {
	return i.ask(prompt, "", hint, validators...)
}

func (i *InteractiveSession) ask(prompt string, def string, hint string, validators ...ValidationFunc) string {
	i.Prompt = prompt
	i.Default = def
	i.ValHint = hint
	i.get()
	for _, validator := range validators {
		if ok, err := validator(i.response); !ok {
			i.Say("\nError: %s\n\n", err)
			i.ask(prompt, def, hint, validators...)
		}
	}
	return i.response
}

// AskPassword is a terminator that asks for a password and does not echo input
// to the terminal.
func (i *InteractiveSession) AskPassword(validators ...ValidationFunc) string {
	rw := bufio.NewReadWriter(i.input, bufio.NewWriter(i.output))
	term := terminal.NewTerminal(rw, "")
	pw, err := term.ReadPassword("Password: ")
	if err != nil {
		i.Error("\n%s\n\n", err)
	}
	for _, validator := range validators {
		if ok, err := validator(strings.TrimSpace(pw)); !ok {
			i.Say("\nError: %s\n\n", err)
			i.AskPassword(validators...)
		}
	}
	return strings.TrimSpace(pw)
}

// AskYesNo asks the user a yes or no question with a default value.  Defaults of `y` or `yes` will
// set the default to yes.  Anything else will default to no.  You can use IsYes or IsNo to act on the response
// without worrying about what version of y, Y, YES, yes, etc. that the user entered.
func (i *InteractiveSession) AskYesNo(prompt string, defaultChoice string) string {
	switch def := strings.ToLower(defaultChoice); def {
	case "y", "yes":
		i.Default = "Y/n"
	default:
		i.Default = "y/N"
	}
	return i.ask(prompt, i.Default, "", ValidateYesNo())
}

// AskFromTable creates a table to select choices from.  It has a built-in validation function that will
// ensure that only the options listed are valid choices.
func (i *InteractiveSession) AskFromTable(prompt string, choices map[string]string, def string) string {
	t := NewTable(2).
		ColumnHeaders("Option", "")
	var allKeys []string
	for key, choice := range choices {
		t.AddRow(key, choice)
		allKeys = append(allKeys, key)
	}
	tAsString := t.AsString()

	i.Prompt = fmt.Sprintf("\n%s%s\nChoice", prompt, tAsString)
	i.Default = def
	i.get()
	if ok, err := AllowedOptions(allKeys)(i.response); !ok {
		i.Say("\nError: %s\n\n", err)
		i.AskFromTable(prompt, choices, def)
	}

	return strings.TrimSpace(i.response)
}
<|endoftext|>