<commit_before>package rrstorage

import (
	"bytes"
	"github.com/cheggaaa/pb"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"github.com/songtianyi/rrframework/logs"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

type UfileStorage struct {
	PublicKey  string
	PrivateKey string
	BucketName string
}

const (
	EXPIRE       = 3600
	SUFFIX       = ".ufile.ucloud.cn"
	MAX_PUT_SIZE = 50 * (1 << 20)
	MAX_GET_SIZE = 50 * (1 << 20)
	PARTIAL_SIZE = 4 * (1 << 20)
)

func CreateUfileStorage(pub, pri, bun string) StorageWrapper {
	s := &UfileStorage{
		PublicKey:  pub,
		PrivateKey: pri,
		BucketName: bun,
	}
	return s
}

func (s *UfileStorage) signheader(method, ctype, bucket, filename string) string {
	data := method + "\n"
	data += "\n"         //Content-MD5 empty
	data += ctype + "\n" //Content-Type
	data += "\n"         //Date empty
	data += "/" + bucket + "/" + filename

	h := hmac.New(sha1.New, []byte(s.PrivateKey))
	h.Write([]byte(data))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

type initResponse struct {
	UploadId string
	BlkSize  int
	Bucket   string
	Key      string
}

func (s *UfileStorage) initiateMultipartUpload(filename string) (*initResponse, error) {
	sign := s.signheader("POST", "application/octet-stream", s.BucketName, filename)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename + "?uploads"
	req, err := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("initiateMultipartUpload failed, %s", string(body))
	}
	var res initResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

type uploadResponse struct {
	PartNumber int
}

func (s *UfileStorage) uploadPart(content []byte, info *initResponse, partNum int) (*uploadResponse, string, error) {
	sign := s.signheader("PUT", "application/octet-stream", info.Bucket, info.Key)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + info.Bucket + SUFFIX + "/" + info.Key + "?uploadId=" + info.UploadId + "&partNumber=" + strconv.Itoa(partNum)
	req, err := http.NewRequest("PUT", url, bytes.NewReader(content))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")
	req.Header.Add("Content-Length", strconv.Itoa(info.BlkSize))

	resp, err := client.Do(req)
	if err != nil {
		return nil, "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, "", err
	}
	if resp.StatusCode != 200 {
		return nil, "", fmt.Errorf("uploadPart failed, %s", string(body))
	}
	var res uploadResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, "", err
	}
	return &res, resp.Header.Get("ETag"), nil
}

type finishResponse struct {
	Bucket   string
	Key      string
	FileSize int
}

func (s *UfileStorage) finishMultipartUpload(info *initResponse, etags string) (*finishResponse, error) {
	sign := s.signheader("POST", "text/plain", info.Bucket, info.Key)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + info.Bucket + SUFFIX + "/" + info.Key + "?uploadId=" + info.UploadId + "&newKey=" + info.Key
	req, err := http.NewRequest("POST", url, strings.NewReader(etags))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Length", strconv.Itoa(len(etags)))
	req.Header.Add("Content-Type", "text/plain")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("finishMultipartUpload failed, %s", string(body))
	}
	var res finishResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

func (s *UfileStorage) put(content []byte, filename string) error {
	// sign
	sign := s.signheader("PUT", "application/octet-stream", s.BucketName, filename)
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename
	req, err := http.NewRequest("PUT", url, bytes.NewReader(content))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")
	req.Header.Add("Content-Length", strconv.Itoa(len(content)))

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		body, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		if err != nil {
			return err
		}
		return fmt.Errorf("put file failed, %s", string(body))
	}
	return nil
}

func (s *UfileStorage) Save(content []byte, filename string) error {

	size := len(content)
	if size > MAX_PUT_SIZE {
		// > 50M
		initRes, err := s.initiateMultipartUpload(filename)
		if err != nil {
			return err
		}
		num := size / initRes.BlkSize
		bar := pb.StartNew(num+1)
		etags := make([]string, 0)
		var wg sync.WaitGroup
		for i := 0; i < num; i++ {
			wg.Add(1)
			go func(j int) {
				defer wg.Done()
				part := content[j*initRes.BlkSize : (j+1)*initRes.BlkSize]
				_, etag, err := s.uploadPart(part, initRes, j)
				if err != nil {
					logs.Error(err)
					return
				}
				etags = append(etags, etag)
				bar.Increment()
			}(i)
		}
		// TODO concurrency limit
		// TODO capture error
		wg.Wait()
		if num*initRes.BlkSize < size {
			// remaining part
			part := content[num*initRes.BlkSize:]
			_, etag, err := s.uploadPart(part, initRes, num)
			if err != nil {
				return err
			}
			etags = append(etags, etag)
			bar.Increment()
		}
		_, err = s.finishMultipartUpload(initRes, strings.Join(etags, ","))
		if err != nil {
			return err
		}
		bar.Finish()

	} else {
		return s.put(content, filename)
	}
	return nil
}

type fileItem struct {
	BucketName string
	FileName   string
	Hash       string
	MimeType   string
	Size       int
	CreateTime int
	ModifyTime int
}

type fileList struct {
	BucketName string
	BucketId   string
	NextMarker string
	DataSet    []fileItem
}

func (s *UfileStorage) PrefixFileList(prefix string) (*fileList, error) {
	// sign
	sign := s.signheader("GET", "", s.BucketName, "")
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/?list&prefix=" + prefix
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", auth)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("PrefixFileList failed, %s", string(body))
	}
	var res fileList
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

func (s *UfileStorage) getFile(filename, brange string) ([]byte, int, error) {
	// sign
	sign := s.signheader("GET", "", s.BucketName, filename)
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", auth)
	req.Header.Add("Range", brange)

	resp, err := client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, 0, err
	}
	if resp.StatusCode != 206 && resp.StatusCode != 200 {
		return nil, 0, fmt.Errorf("getFile failed, %s", string(body))
	}
	size := 0
	if resp.StatusCode == 200 {
		// complete
		size, _ = strconv.Atoi(resp.Header.Get("Content-Length"))
	} else if resp.StatusCode == 206 {
		// partial
		size, _ = strconv.Atoi(strings.Split(resp.Header.Get("Content-Range"), "/")[1])
	}
	return body, size, nil
}

func (s *UfileStorage) Fetch(filename string) ([]byte, error) {
	b, size, err := s.getFile(filename, "bytes=0-"+strconv.Itoa(MAX_GET_SIZE-1))
	if err != nil {
		return b, err
	}
	lb := len(b)
	if lb == size {
		// downloaded
		return b, nil
	}
	// partial
	size -= lb
	num := size / PARTIAL_SIZE
	bar := pb.StartNew(num+1)
	// TODO concurrency
	for i := 0; i <= num; i++ {
		brange := "bytes="
		brange += strconv.Itoa(i*PARTIAL_SIZE+lb) + "-"
		brange += strconv.Itoa((i+1)*PARTIAL_SIZE + lb - 1)
		bp, _, err := s.getFile(filename, brange)
		if err != nil {
			logs.Error(err)
			continue
		}
		b = append(b, bp...)
		bar.Increment()
	}
	bar.Finish()
	return b, nil
}
<commit_msg>add ufile uploading concurrency limit<commit_after>package rrstorage

import (
	"bytes"
	"github.com/cheggaaa/pb"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"github.com/songtianyi/rrframework/logs"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

type UfileStorage struct {
	PublicKey  string
	PrivateKey string
	BucketName string

	usema chan struct{}	// uploading concurrency limit
}

const (
	EXPIRE       = 3600
	SUFFIX       = ".ufile.ucloud.cn"
	MAX_PUT_SIZE = 50 * (1 << 20)
	MAX_GET_SIZE = 50 * (1 << 20)
	PARTIAL_SIZE = 4 * (1 << 20)
)

func CreateUfileStorage(pub, pri, bun string, ucl int) StorageWrapper {
	s := &UfileStorage{
		PublicKey:  pub,
		PrivateKey: pri,
		BucketName: bun,
		usema: make(chan struct{}, ucl),
	}
	return s
}

func (s *UfileStorage) signheader(method, ctype, bucket, filename string) string {
	data := method + "\n"
	data += "\n"         //Content-MD5 empty
	data += ctype + "\n" //Content-Type
	data += "\n"         //Date empty
	data += "/" + bucket + "/" + filename

	h := hmac.New(sha1.New, []byte(s.PrivateKey))
	h.Write([]byte(data))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

type initResponse struct {
	UploadId string
	BlkSize  int
	Bucket   string
	Key      string
}

func (s *UfileStorage) initiateMultipartUpload(filename string) (*initResponse, error) {
	sign := s.signheader("POST", "application/octet-stream", s.BucketName, filename)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename + "?uploads"
	req, err := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("initiateMultipartUpload failed, %s", string(body))
	}
	var res initResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

type uploadResponse struct {
	PartNumber int
}

func (s *UfileStorage) uploadPart(content []byte, info *initResponse, partNum int) (*uploadResponse, string, error) {
	sign := s.signheader("PUT", "application/octet-stream", info.Bucket, info.Key)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + info.Bucket + SUFFIX + "/" + info.Key + "?uploadId=" + info.UploadId + "&partNumber=" + strconv.Itoa(partNum)
	req, err := http.NewRequest("PUT", url, bytes.NewReader(content))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")
	req.Header.Add("Content-Length", strconv.Itoa(info.BlkSize))

	resp, err := client.Do(req)
	if err != nil {
		return nil, "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, "", err
	}
	if resp.StatusCode != 200 {
		return nil, "", fmt.Errorf("uploadPart failed, %s", string(body))
	}
	var res uploadResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, "", err
	}
	return &res, resp.Header.Get("ETag"), nil
}

type finishResponse struct {
	Bucket   string
	Key      string
	FileSize int
}

func (s *UfileStorage) finishMultipartUpload(info *initResponse, etags string) (*finishResponse, error) {
	sign := s.signheader("POST", "text/plain", info.Bucket, info.Key)

	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + info.Bucket + SUFFIX + "/" + info.Key + "?uploadId=" + info.UploadId + "&newKey=" + info.Key
	req, err := http.NewRequest("POST", url, strings.NewReader(etags))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Length", strconv.Itoa(len(etags)))
	req.Header.Add("Content-Type", "text/plain")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("finishMultipartUpload failed, %s", string(body))
	}
	var res finishResponse
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

func (s *UfileStorage) put(content []byte, filename string) error {
	// sign
	sign := s.signheader("PUT", "application/octet-stream", s.BucketName, filename)
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename
	req, err := http.NewRequest("PUT", url, bytes.NewReader(content))

	req.Header.Add("Authorization", auth)
	req.Header.Add("Content-Type", "application/octet-stream")
	req.Header.Add("Content-Length", strconv.Itoa(len(content)))

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		body, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		if err != nil {
			return err
		}
		return fmt.Errorf("put file failed, %s", string(body))
	}
	return nil
}

func (s *UfileStorage) Save(content []byte, filename string) error {

	size := len(content)
	if size > MAX_PUT_SIZE {
		// > 50M
		initRes, err := s.initiateMultipartUpload(filename)
		if err != nil {
			return err
		}
		num := size / initRes.BlkSize
		bar := pb.StartNew(num+1)
		etags := make([]string, 0)
		var wg sync.WaitGroup
		for i := 0; i < num; i++ {
			s.usema <- struct{}{}
			wg.Add(1)
			go func(j int) {
				defer func() {
					wg.Done()
					<-s.usema
				}()
				part := content[j*initRes.BlkSize : (j+1)*initRes.BlkSize]
				_, etag, err := s.uploadPart(part, initRes, j)
				if err != nil {
					logs.Error(err)
					return
				}
				etags = append(etags, etag)
				bar.Increment()
			}(i)
		}
		// TODO capture error
		wg.Wait()
		if num*initRes.BlkSize < size {
			// remaining part
			part := content[num*initRes.BlkSize:]
			_, etag, err := s.uploadPart(part, initRes, num)
			if err != nil {
				return err
			}
			etags = append(etags, etag)
			bar.Increment()
		}
		_, err = s.finishMultipartUpload(initRes, strings.Join(etags, ","))
		if err != nil {
			return err
		}
		bar.Finish()

	} else {
		return s.put(content, filename)
	}
	return nil
}

type fileItem struct {
	BucketName string
	FileName   string
	Hash       string
	MimeType   string
	Size       int
	CreateTime int
	ModifyTime int
}

type fileList struct {
	BucketName string
	BucketId   string
	NextMarker string
	DataSet    []fileItem
}

func (s *UfileStorage) PrefixFileList(prefix string) (*fileList, error) {
	// sign
	sign := s.signheader("GET", "", s.BucketName, "")
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/?list&prefix=" + prefix
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", auth)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("PrefixFileList failed, %s", string(body))
	}
	var res fileList
	if err := json.Unmarshal(body, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

func (s *UfileStorage) getFile(filename, brange string) ([]byte, int, error) {
	// sign
	sign := s.signheader("GET", "", s.BucketName, filename)
	auth := "UCloud" + " " + s.PublicKey + ":" + sign
	client := &http.Client{}
	url := "http://" + s.BucketName + SUFFIX + "/" + filename
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", auth)
	req.Header.Add("Range", brange)

	resp, err := client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, 0, err
	}
	if resp.StatusCode != 206 && resp.StatusCode != 200 {
		return nil, 0, fmt.Errorf("getFile failed, %s", string(body))
	}
	size := 0
	if resp.StatusCode == 200 {
		// complete
		size, _ = strconv.Atoi(resp.Header.Get("Content-Length"))
	} else if resp.StatusCode == 206 {
		// partial
		size, _ = strconv.Atoi(strings.Split(resp.Header.Get("Content-Range"), "/")[1])
	}
	return body, size, nil
}

func (s *UfileStorage) Fetch(filename string) ([]byte, error) {
	b, size, err := s.getFile(filename, "bytes=0-"+strconv.Itoa(MAX_GET_SIZE-1))
	if err != nil {
		return b, err
	}
	lb := len(b)
	if lb == size {
		// downloaded
		return b, nil
	}
	// partial
	size -= lb
	num := size / PARTIAL_SIZE
	bar := pb.StartNew(num+1)
	// TODO concurrency
	for i := 0; i <= num; i++ {
		brange := "bytes="
		brange += strconv.Itoa(i*PARTIAL_SIZE+lb) + "-"
		brange += strconv.Itoa((i+1)*PARTIAL_SIZE + lb - 1)
		bp, _, err := s.getFile(filename, brange)
		if err != nil {
			logs.Error(err)
			continue
		}
		b = append(b, bp...)
		bar.Increment()
	}
	bar.Finish()
	return b, nil
}
<|endoftext|>


<commit_before>package sdl

/*
#include "sdl_wrapper.h"

static inline int _SDL_GetSystemRAM()
{
#if (SDL_VERSION_ATLEAST(2,0,1))
    return SDL_GetSystemRAM();
#else
    return 0;
#endif
}

static inline SDL_bool _SDL_HasAVX()
{
#if (SDL_VERSION_ATLEAST(2,0,2))
    return SDL_HasAVX();
#else
    return SDL_FALSE;
#endif
}

*/
import "C"

const CACHELINE_SIZE = C.SDL_CACHELINE_SIZE

// GetCPUCount (https://wiki.libsdl.org/SDL_GetCPUCount)
func GetCPUCount() int {
	return int(C.SDL_GetCPUCount())
}

// GetCPUCacheLineSize (https://wiki.libsdl.org/SDL_GetCPUCacheLineSize)
func GetCPUCacheLineSize() int {
	return int(C.SDL_GetCPUCacheLineSize())
}

// HasRDTSC (https://wiki.libsdl.org/SDL_HasRDTSC)
func HasRDTSC() bool {
	return C.SDL_HasRDTSC() > 0
}

// HasAltiVec (https://wiki.libsdl.org/SDL_HasAltiVec)
func HasAltiVec() bool {
	return C.SDL_HasAltiVec() > 0
}

// HasMMX (https://wiki.libsdl.org/SDL_HasMMX)
func HasMMX() bool {
	return C.SDL_HasMMX() > 0
}

// Has3DNow (https://wiki.libsdl.org/SDL_Has3DNow)
func Has3DNow() bool {
	return C.SDL_Has3DNow() > 0
}

// HasSSE (https://wiki.libsdl.org/SDL_HasSSE)
func HasSSE() bool {
	return C.SDL_HasSSE() > 0
}

// HasSSE2 (https://wiki.libsdl.org/SDL_HasSSE2)
func HasSSE2() bool {
	return C.SDL_HasSSE2() > 0
}

// HasSSE3 (https://wiki.libsdl.org/SDL_HasSSE3)
func HasSSE3() bool {
	return C.SDL_HasSSE3() > 0
}

// HasSSE41 (https://wiki.libsdl.org/SDL_HasSSE41)
func HasSSE41() bool {
	return C.SDL_HasSSE41() > 0
}

// HasSSE42 (https://wiki.libsdl.org/SDL_HasSSE42)
func HasSSE42() bool {
	return C.SDL_HasSSE42() > 0
}

// GetSystemRAM (https://wiki.libsdl.org/SDL_GetSystemRAM)
func GetSystemRAM() int {
	return int(C._SDL_GetSystemRAM())
}

func HasAVX() bool {
	return C._SDL_HasAVX() > 0
}
<commit_msg>sdl: cpuinfo: add warning message to SDL_GetSystemRAM() and SDL_HasAVX() if used on older SDL2 versions<commit_after>package sdl

/*
#include "sdl_wrapper.h"

#if !(SDL_VERSION_ATLEAST(2,0,1))
#pragma message("SDL_GetSystemRAM is not supported before SDL 2.0.1")
static inline int SDL_GetSystemRAM()
{
	return -1;
}
#endif

#if !(SDL_VERSION_ATLEAST(2,0,2))
#pragma message("SDL_HasAVX is not supported before SDL 2.0.2")
static inline SDL_bool SDL_HasAVX()
{
	return SDL_FALSE;
}
#endif

*/
import "C"

const CACHELINE_SIZE = C.SDL_CACHELINE_SIZE

// GetCPUCount (https://wiki.libsdl.org/SDL_GetCPUCount)
func GetCPUCount() int {
	return int(C.SDL_GetCPUCount())
}

// GetCPUCacheLineSize (https://wiki.libsdl.org/SDL_GetCPUCacheLineSize)
func GetCPUCacheLineSize() int {
	return int(C.SDL_GetCPUCacheLineSize())
}

// HasRDTSC (https://wiki.libsdl.org/SDL_HasRDTSC)
func HasRDTSC() bool {
	return C.SDL_HasRDTSC() > 0
}

// HasAltiVec (https://wiki.libsdl.org/SDL_HasAltiVec)
func HasAltiVec() bool {
	return C.SDL_HasAltiVec() > 0
}

// HasMMX (https://wiki.libsdl.org/SDL_HasMMX)
func HasMMX() bool {
	return C.SDL_HasMMX() > 0
}

// Has3DNow (https://wiki.libsdl.org/SDL_Has3DNow)
func Has3DNow() bool {
	return C.SDL_Has3DNow() > 0
}

// HasSSE (https://wiki.libsdl.org/SDL_HasSSE)
func HasSSE() bool {
	return C.SDL_HasSSE() > 0
}

// HasSSE2 (https://wiki.libsdl.org/SDL_HasSSE2)
func HasSSE2() bool {
	return C.SDL_HasSSE2() > 0
}

// HasSSE3 (https://wiki.libsdl.org/SDL_HasSSE3)
func HasSSE3() bool {
	return C.SDL_HasSSE3() > 0
}

// HasSSE41 (https://wiki.libsdl.org/SDL_HasSSE41)
func HasSSE41() bool {
	return C.SDL_HasSSE41() > 0
}

// HasSSE42 (https://wiki.libsdl.org/SDL_HasSSE42)
func HasSSE42() bool {
	return C.SDL_HasSSE42() > 0
}

// GetSystemRAM (https://wiki.libsdl.org/SDL_GetSystemRAM)
func GetSystemRAM() int {
	return int(C.SDL_GetSystemRAM())
}

func HasAVX() bool {
	return C.SDL_HasAVX() > 0
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
)

// configureEnv is a cache of environment variables that is
// reused when configuring multiple packages.
type configureEnv struct {
	envSansPkgConfigPath []string
	origPkgConfigPath    string // original value of PKG_CONFIG_PATH
	buildDir             string
	pkgBuildDir          map[string]string
}

const pkgConfigPathVarName = "PKG_CONFIG_PATH"

func dropEnvVar(env []string, i int) []string {
	if i < len(env)-1 {
		env[i] = env[len(env)-1]
	}
	return env[:len(env)-1]
}

func prepareConfigureEnv(buildDir string) *configureEnv {
	ce := &configureEnv{
		os.Environ(),
		"",
		buildDir,
		map[string]string{}}

	for i, v := range ce.envSansPkgConfigPath {
		if strings.HasPrefix(v, pkgConfigPathVarName+"=") {
			ce.envSansPkgConfigPath = dropEnvVar(
				ce.envSansPkgConfigPath, i)
			ce.origPkgConfigPath = strings.SplitN(v, "=", 2)[1]
			break
		}
	}

	return ce
}

func (ce *configureEnv) addPackageBuildDir(pkgName string) {
	ce.pkgBuildDir[pkgName] = path.Join(ce.buildDir, pkgName)
}

func (ce *configureEnv) makeEnv(pd *packageDefinition) []string {
	var configuredPackagePathames []string

	for _, dep := range pd.allRequired {
		depBuildDir, found := ce.pkgBuildDir[dep.PackageName]
		if found {
			configuredPackagePathames = append(
				configuredPackagePathames, depBuildDir)
		}
	}

	pkgConfigPath := strings.Join(configuredPackagePathames, ":")

	if len(pkgConfigPath) == 0 {
		if len(ce.origPkgConfigPath) == 0 {
			return ce.envSansPkgConfigPath
		}
		pkgConfigPath = ce.origPkgConfigPath
	} else if len(ce.origPkgConfigPath) > 0 {
		pkgConfigPath += ":"
		pkgConfigPath += ce.origPkgConfigPath
	}

	return append(ce.envSansPkgConfigPath,
		pkgConfigPathVarName+"="+pkgConfigPath)
}

func configurePackage(installDir, pkgRootDir string, pd *packageDefinition,
	cfgEnv *configureEnv, conftab *Conftab) error {
	fmt.Println("[configure] " + pd.PackageName)

	pkgBuildDir := path.Join(cfgEnv.buildDir, pd.PackageName)

	configurePathname, err := filepath.Rel(pkgBuildDir,
		path.Join(pkgRootDir, pd.PackageName, "configure"))
	if err != nil {
		return err
	}

	err = os.MkdirAll(pkgBuildDir, os.FileMode(0775))
	if err != nil {
		return nil
	}

	configureArgs := conftab.getConfigureArgs(pd.PackageName)
	configureArgs = append(configureArgs, "--quiet", "--prefix="+installDir)

	configureCmd := exec.Command(configurePathname, configureArgs...)
	configureCmd.Dir = pkgBuildDir
	configureCmd.Stdout = os.Stdout
	configureCmd.Stderr = os.Stderr
	configureCmd.Env = cfgEnv.makeEnv(pd)
	if err := configureCmd.Run(); err != nil {
		return errors.New(configurePathname + ": " + err.Error())
	}

	return nil
}

func configurePackages(args []string) error {
	ws, err := loadWorkspace()
	if err != nil {
		return err
	}

	pi, err := readPackageDefinitions(ws.wp)
	if err != nil {
		return err
	}

	buildDir := ws.buildDir()

	cfgEnv := prepareConfigureEnv(buildDir)

	if configuredPackageDirs, err := ioutil.ReadDir(buildDir); err == nil {
		// Register packages that already exist
		// in the build directory.
		for _, dir := range configuredPackageDirs {
			cfgEnv.addPackageBuildDir(dir.Name())
		}
	}

	var selection packageDefinitionList

	if len(args) > 0 {
		selection, err = packageRangesToFlatSelection(pi, args)
	} else {
		selection, err = readPackageSelection(pi, ws.absPrivateDir)
	}
	if err != nil {
		return err
	}

	for _, pd := range selection {
		cfgEnv.addPackageBuildDir(pd.PackageName)
	}

	conftab, err := readConftab(
		path.Join(ws.absPrivateDir, conftabFilename))
	if err != nil {
		return err
	}

	installDir := ws.installDir()
	pkgRootDir := ws.generatedPkgRootDir()

	for _, pd := range selection {
		err := configurePackage(installDir, pkgRootDir, pd,
			cfgEnv, conftab)
		if err != nil {
			return err
		}
	}

	return nil
}

// configureCmd represents the configure command
var configureCmd = &cobra.Command{
	Use: "configure [package_range...]",
	Short: "Configure all selected packages " +
		"or the specified package range",
	Run: func(_ *cobra.Command, args []string) {
		if err := configurePackages(args); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	rootCmd.AddCommand(configureCmd)

	configureCmd.Flags().SortFlags = false
	addQuietFlag(configureCmd)
	addWorkspaceDirFlag(configureCmd)
}
<commit_msg>Use absolute pathname for configure script<commit_after>// Copyright (C) 2017, 2018 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/spf13/cobra"
)

// configureEnv is a cache of environment variables that is
// reused when configuring multiple packages.
type configureEnv struct {
	envSansPkgConfigPath []string
	origPkgConfigPath    string // original value of PKG_CONFIG_PATH
	buildDir             string
	pkgBuildDir          map[string]string
}

const pkgConfigPathVarName = "PKG_CONFIG_PATH"

func dropEnvVar(env []string, i int) []string {
	if i < len(env)-1 {
		env[i] = env[len(env)-1]
	}
	return env[:len(env)-1]
}

func prepareConfigureEnv(buildDir string) *configureEnv {
	ce := &configureEnv{
		os.Environ(),
		"",
		buildDir,
		map[string]string{}}

	for i, v := range ce.envSansPkgConfigPath {
		if strings.HasPrefix(v, pkgConfigPathVarName+"=") {
			ce.envSansPkgConfigPath = dropEnvVar(
				ce.envSansPkgConfigPath, i)
			ce.origPkgConfigPath = strings.SplitN(v, "=", 2)[1]
			break
		}
	}

	return ce
}

func (ce *configureEnv) addPackageBuildDir(pkgName string) {
	ce.pkgBuildDir[pkgName] = path.Join(ce.buildDir, pkgName)
}

func (ce *configureEnv) makeEnv(pd *packageDefinition) []string {
	var configuredPackagePathames []string

	for _, dep := range pd.allRequired {
		depBuildDir, found := ce.pkgBuildDir[dep.PackageName]
		if found {
			configuredPackagePathames = append(
				configuredPackagePathames, depBuildDir)
		}
	}

	pkgConfigPath := strings.Join(configuredPackagePathames, ":")

	if len(pkgConfigPath) == 0 {
		if len(ce.origPkgConfigPath) == 0 {
			return ce.envSansPkgConfigPath
		}
		pkgConfigPath = ce.origPkgConfigPath
	} else if len(ce.origPkgConfigPath) > 0 {
		pkgConfigPath += ":"
		pkgConfigPath += ce.origPkgConfigPath
	}

	return append(ce.envSansPkgConfigPath,
		pkgConfigPathVarName+"="+pkgConfigPath)
}

func configurePackage(installDir, pkgRootDir string, pd *packageDefinition,
	cfgEnv *configureEnv, conftab *Conftab) error {
	fmt.Println("[configure] " + pd.PackageName)

	configurePathname := path.Join(pkgRootDir, pd.PackageName, "configure")

	pkgBuildDir := path.Join(cfgEnv.buildDir, pd.PackageName)

	err := os.MkdirAll(pkgBuildDir, os.FileMode(0775))
	if err != nil {
		return nil
	}

	configureArgs := conftab.getConfigureArgs(pd.PackageName)
	configureArgs = append(configureArgs, "--quiet", "--prefix="+installDir)

	configureCmd := exec.Command(configurePathname, configureArgs...)
	configureCmd.Dir = pkgBuildDir
	configureCmd.Stdout = os.Stdout
	configureCmd.Stderr = os.Stderr
	configureCmd.Env = cfgEnv.makeEnv(pd)
	if err := configureCmd.Run(); err != nil {
		return errors.New(configurePathname + ": " + err.Error())
	}

	return nil
}

func configurePackages(args []string) error {
	ws, err := loadWorkspace()
	if err != nil {
		return err
	}

	pi, err := readPackageDefinitions(ws.wp)
	if err != nil {
		return err
	}

	buildDir := ws.buildDir()

	cfgEnv := prepareConfigureEnv(buildDir)

	if configuredPackageDirs, err := ioutil.ReadDir(buildDir); err == nil {
		// Register packages that already exist
		// in the build directory.
		for _, dir := range configuredPackageDirs {
			cfgEnv.addPackageBuildDir(dir.Name())
		}
	}

	var selection packageDefinitionList

	if len(args) > 0 {
		selection, err = packageRangesToFlatSelection(pi, args)
	} else {
		selection, err = readPackageSelection(pi, ws.absPrivateDir)
	}
	if err != nil {
		return err
	}

	for _, pd := range selection {
		cfgEnv.addPackageBuildDir(pd.PackageName)
	}

	conftab, err := readConftab(
		path.Join(ws.absPrivateDir, conftabFilename))
	if err != nil {
		return err
	}

	installDir := ws.installDir()
	pkgRootDir := ws.generatedPkgRootDir()

	for _, pd := range selection {
		err := configurePackage(installDir, pkgRootDir, pd,
			cfgEnv, conftab)
		if err != nil {
			return err
		}
	}

	return nil
}

// configureCmd represents the configure command
var configureCmd = &cobra.Command{
	Use: "configure [package_range...]",
	Short: "Configure all selected packages " +
		"or the specified package range",
	Run: func(_ *cobra.Command, args []string) {
		if err := configurePackages(args); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	rootCmd.AddCommand(configureCmd)

	configureCmd.Flags().SortFlags = false
	addQuietFlag(configureCmd)
	addWorkspaceDirFlag(configureCmd)
}
<|endoftext|>


<commit_before>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		log.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		}
	}

	return
}

// send quit to the server and close the connection
func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

// rename file
func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}

// make directory
func (ftp *FTP) Mkd(path string) error {
	_, err := ftp.cmd("257", "MKD %s", path)
	return err
}

// get current path
func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

// change current path
func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

// delete file
func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

// secures the ftp connection by using TLS
func (ftp *FTP) AuthTLS(config tls.Config) error {
	if _, err := ftp.cmd("234", "AUTH TLS"); err != nil {
		return err
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err := ftp.cmd("200", "PBSZ 0"); err != nil {
		return err
	}

	if _, err := ftp.cmd("200", "PROT P"); err != nil {
		return err
	}

	return nil
}

func (ftp *FTP) ReadAndDiscard() (int, error) {
	var i int
	var err error
	buffer_size:=ftp.reader.Buffered()
	for i = 0; i < buffer_size; i++ {
		if _, err=ftp.reader.ReadByte(); err != nil {
			return i,err
		}
    }
	return i,err
}

// change transfer type
func (ftp *FTP) Type(t string) error {
	_, err := ftp.cmd("200", "TYPE %s", t)
	return err
}

func (ftp *FTP) receiveLine() (string, error) {
	line, err := ftp.reader.ReadString('\n')

	if ftp.debug {
		log.Printf("< %s", line)
	}

	return line, err
}

func (ftp *FTP) receive() (string, error) {
	line, err := ftp.receiveLine()

	if err != nil {
		return line, err
	}

	if (len(line) >= 4) && (line[3] == '-') {
		nextLine := ""
		// This is a continuation of output line
		nextLine, err = ftp.receive()
		line = line + nextLine
	}

	return line, err
}

func (ftp *FTP) send(command string, arguments ...interface{}) error {
	if ftp.debug {
		log.Printf("> %s", fmt.Sprintf(command, arguments...))
	}

	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if _, err := ftp.writer.WriteString(command); err != nil {
		return err
	}

	if err := ftp.writer.Flush(); err != nil {
		return err
	}

	return nil
}

// enables passive data connection and returns port number
func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

// open new data connection
func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	addr := fmt.Sprintf("%s:%d", strings.Split(ftp.addr, ":")[0], port)

	if ftp.debug {
		log.Printf("Connecting to %s\n", addr)
	}

	if conn, err = net.Dial("tcp", addr); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

// upload file
func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

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

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

// retrieves file
func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// list the path (or current directory)
func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// check if MLSD works
	if err = ftp.send("MLSD %s", path); err != nil {
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		// MLSD failed, lets try LIST
		if err = ftp.send("LIST %s", path); err != nil {
			return
		}

		if line, err = ftp.receive(); err != nil {
			return
		}

		if !strings.HasPrefix(line, "150") {
			// Really list is not working here
			err = errors.New(line)
			return
		}
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// login to the server
func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		if strings.HasPrefix(err.Error(), "230") {
			// Ok, probably anonymous server
			// but login was fine, so return no error
			err = nil
		} else {
			return
		}
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

// connect to server, debug is OFF
func Connect(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	reader.ReadString('\n')

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: false}, nil
}

// connect to server, debug is ON
func ConnectDbg(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')

	log.Print(line)

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: true}, nil
}
<commit_msg>still not work<commit_after>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		log.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		}
	}

	return
}

// send quit to the server and close the connection
func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

// rename file
func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}

// make directory
func (ftp *FTP) Mkd(path string) error {
	_, err := ftp.cmd("257", "MKD %s", path)
	return err
}

// get current path
func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

// change current path
func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

// delete file
func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

// secures the ftp connection by using TLS
func (ftp *FTP) AuthTLS(config tls.Config) error {
	if _, err := ftp.cmd("234", "AUTH TLS"); err != nil {
		return err
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err := ftp.cmd("200", "PBSZ 0"); err != nil {
		return err
	}

	if _, err := ftp.cmd("200", "PROT P"); err != nil {
		return err
	}

	return nil
}

// read all the buffered bytes and return
func (ftp *FTP) ReadAndDiscard() (int, error) {
	fmt.Println("svuoto")
	var i int
	var err error
	buffer_size := ftp.reader.Buffered()
	for i = 0; i < buffer_size; i++ {
		if _, err = ftp.reader.ReadByte(); err != nil {
			return i, err
		}
	}
	return i, err
}

// change transfer type
func (ftp *FTP) Type(t string) error {
	_, err := ftp.cmd("200", "TYPE %s", t)
	return err
}

func (ftp *FTP) receiveLine() (string, error) {
	line, err := ftp.reader.ReadString('\n')

	if ftp.debug {
		log.Printf("< %s", line)
	}

	return line, err
}

func (ftp *FTP) receive() (string, error) {
	line, err := ftp.receiveLine()

	if err != nil {
		return line, err
	}

	if (len(line) >= 4) && (line[3] == '-') {
		nextLine := ""
		// This is a continuation of output line
		nextLine, err = ftp.receive()
		line = line + nextLine
	}

	return line, err
}

func (ftp *FTP) send(command string, arguments ...interface{}) error {
	if ftp.debug {
		log.Printf("> %s", fmt.Sprintf(command, arguments...))
	}

	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if _, err := ftp.writer.WriteString(command); err != nil {
		return err
	}

	if err := ftp.writer.Flush(); err != nil {
		return err
	}

	return nil
}

// enables passive data connection and returns port number
func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

// open new data connection
func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	addr := fmt.Sprintf("%s:%d", strings.Split(ftp.addr, ":")[0], port)

	if ftp.debug {
		log.Printf("Connecting to %s\n", addr)
	}

	if conn, err = net.Dial("tcp", addr); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

// upload file
func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

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

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

// retrieves file
func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// list the path (or current directory)
func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// check if MLSD works
	if err = ftp.send("MLSD %s", path); err != nil {
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		// MLSD failed, lets try LIST
		if err = ftp.send("LIST %s", path); err != nil {
			return
		}

		if line, err = ftp.receive(); err != nil {
			return
		}

		if !strings.HasPrefix(line, "150") {
			// Really list is not working here
			err = errors.New(line)
			return
		}
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// login on server with strange login behavior
func (ftp *FTP) SmartLogin(username string, password string) (err error) {
	// Maybe the server has some useless words to say. Make him talk
	err = ftp.Noop()
	if err != nil && strings.HasPrefix(err.Error(), "220") {
		// Maybe with another Noop the server will ask us to login?
		err = ftp.Noop()
		if err != nil && strings.HasPrefix(err.Error(), "530") {
			// ok, let's login

			//ftp.Login(username, password)

			if _, err = ftp.cmd("530", "USER %s", username); err != nil {
				return
			}
			if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
				return
			}
		}

	}
	// Nothing strange... let's try a normal login
	return ftp.Login(username, password)
}

// login to the server
func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		if strings.HasPrefix(err.Error(), "230") {
			// Ok, probably anonymous server
			// but login was fine, so return no error
			err = nil
		} else {
			return
		}
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

// connect to server, debug is OFF
func Connect(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	reader.ReadString('\n')

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: false}, nil
}

// connect to server, debug is ON
func ConnectDbg(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')

	log.Print(line)

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: true}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"syscall"
	"unsafe"
)

const (
	ansiEraseDisplay = "\033[2J"
	ansiResetCursor  = "\033[H"
	carriageReturn   = "\015"
	defaultPrompt    = ">> "
)

var originalSttyState bytes.Buffer
var winRows uint16
var winCols uint16

type winsize struct {
	rows, cols, xpixel, ypixel uint16
}

func getWinsize() winsize {
	ws := winsize{}
	syscall.Syscall(syscall.SYS_IOCTL,
		uintptr(0), uintptr(syscall.TIOCGWINSZ),
		uintptr(unsafe.Pointer(&ws)))
	return ws
}

// TODO: This is wrong: stdin should be the TTY
func getSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", "-g")
	cmd.Stdin = os.Stdin
	cmd.Stdout = state
	return cmd.Run()
}

// TODO: This is wrong: stdin and stdout should be the TTY
func setSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", state.String())
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func NewTTY() (t *TTY, err error) {
	fh, err := os.OpenFile("/dev/tty", os.O_RDWR, 0666)
	if err != nil {
		return
	}
	t = &TTY{fh, defaultPrompt}
	return
}

type TTY struct {
	*os.File
	prompt string
}

// Clears the screen and sets the cursor to first row, first column
func (t *TTY) resetScreen() {
	fmt.Fprint(t.File, ansiEraseDisplay+ansiResetCursor)
}

// Print prompt with `in`
func (t *TTY) printPrompt(in []byte) {
	fmt.Fprintf(t.File, t.prompt+"%s", in)
}

// Positions the cursor after the prompt and `inlen` colums to the right
func (t *TTY) cursorAfterPrompt(inlen int) {
	t.setCursorPos(0, len(t.prompt)+inlen)
}

// Sets the cursor to `line` and `col`
func (t *TTY) setCursorPos(line int, col int) {
	fmt.Fprintf(t.File, "\033[%d;%dH", line+1, col+1)
}

func init() {
	ws := getWinsize()
	winRows = ws.rows
	winCols = ws.cols
}

func main() {
	err := getSttyState(&originalSttyState)
	if err != nil {
		log.Fatal(err)
	}
	// TODO: this needs to be run when the process is interrupted
	defer setSttyState(&originalSttyState)

	setSttyState(bytes.NewBufferString("cbreak"))
	setSttyState(bytes.NewBufferString("-echo"))

	cmdTemplate := "ag {{}}"
	placeholder := "{{}}"

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}

	printer := NewPrinter(tty, int(winCols), int(winRows)-3)

	runner := &Runner{
		printer:     printer,
		template:    cmdTemplate,
		placeholder: placeholder,
		buf:         new(bytes.Buffer),
	}

	// TODO: Clean this up. This is a mess.
	var input []byte = make([]byte, 0)
	var b []byte = make([]byte, 1)

	for {
		tty.resetScreen()
		tty.printPrompt(input[:len(input)])

		if len(input) > 0 {
			runner.killCurrent()

			fmt.Fprintf(tty, "\n")

			go func() {
				runner.runWithInput(input[:len(input)])
				tty.cursorAfterPrompt(len(input))
			}()
		}

		os.Stdin.Read(b)
		switch b[0] {
		case 127:
			// Backspace
			if len(input) > 1 {
				input = input[:len(input)-1]
			} else if len(input) == 1 {
				input = nil
			}
		case 4, 10, 13:
			// Ctrl-D, line feed, carriage return
			runner.writeCmdStdout(os.Stdout)
			return
		default:
			// TODO: Default is wrong here. Only append printable characters to
			// input
			input = append(input, b...)
		}
	}
}
<commit_msg>Reset screen before printing final output<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"syscall"
	"unsafe"
)

const (
	ansiEraseDisplay = "\033[2J"
	ansiResetCursor  = "\033[H"
	carriageReturn   = "\015"
	defaultPrompt    = ">> "
)

var originalSttyState bytes.Buffer
var winRows uint16
var winCols uint16

type winsize struct {
	rows, cols, xpixel, ypixel uint16
}

func getWinsize() winsize {
	ws := winsize{}
	syscall.Syscall(syscall.SYS_IOCTL,
		uintptr(0), uintptr(syscall.TIOCGWINSZ),
		uintptr(unsafe.Pointer(&ws)))
	return ws
}

// TODO: This is wrong: stdin should be the TTY
func getSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", "-g")
	cmd.Stdin = os.Stdin
	cmd.Stdout = state
	return cmd.Run()
}

// TODO: This is wrong: stdin and stdout should be the TTY
func setSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", state.String())
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func NewTTY() (t *TTY, err error) {
	fh, err := os.OpenFile("/dev/tty", os.O_RDWR, 0666)
	if err != nil {
		return
	}
	t = &TTY{fh, defaultPrompt}
	return
}

type TTY struct {
	*os.File
	prompt string
}

// Clears the screen and sets the cursor to first row, first column
func (t *TTY) resetScreen() {
	fmt.Fprint(t.File, ansiEraseDisplay+ansiResetCursor)
}

// Print prompt with `in`
func (t *TTY) printPrompt(in []byte) {
	fmt.Fprintf(t.File, t.prompt+"%s", in)
}

// Positions the cursor after the prompt and `inlen` colums to the right
func (t *TTY) cursorAfterPrompt(inlen int) {
	t.setCursorPos(0, len(t.prompt)+inlen)
}

// Sets the cursor to `line` and `col`
func (t *TTY) setCursorPos(line int, col int) {
	fmt.Fprintf(t.File, "\033[%d;%dH", line+1, col+1)
}

func init() {
	ws := getWinsize()
	winRows = ws.rows
	winCols = ws.cols
}

func main() {
	err := getSttyState(&originalSttyState)
	if err != nil {
		log.Fatal(err)
	}
	// TODO: this needs to be run when the process is interrupted
	defer setSttyState(&originalSttyState)

	setSttyState(bytes.NewBufferString("cbreak"))
	setSttyState(bytes.NewBufferString("-echo"))

	cmdTemplate := "ag {{}}"
	placeholder := "{{}}"

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}

	printer := NewPrinter(tty, int(winCols), int(winRows)-3)

	runner := &Runner{
		printer:     printer,
		template:    cmdTemplate,
		placeholder: placeholder,
		buf:         new(bytes.Buffer),
	}

	// TODO: Clean this up. This is a mess.
	var input []byte = make([]byte, 0)
	var b []byte = make([]byte, 1)

	for {
		tty.resetScreen()
		tty.printPrompt(input[:len(input)])

		if len(input) > 0 {
			runner.killCurrent()

			fmt.Fprintf(tty, "\n")

			go func() {
				runner.runWithInput(input[:len(input)])
				tty.cursorAfterPrompt(len(input))
			}()
		}

		os.Stdin.Read(b)
		switch b[0] {
		case 127:
			// Backspace
			if len(input) > 1 {
				input = input[:len(input)-1]
			} else if len(input) == 1 {
				input = nil
			}
		case 4, 10, 13:
			// Ctrl-D, line feed, carriage return
			tty.resetScreen()
			runner.writeCmdStdout(os.Stdout)
			return
		default:
			// TODO: Default is wrong here. Only append printable characters to
			// input
			input = append(input, b...)
		}
	}
}
<|endoftext|>


<commit_before>package sus

import(
	`github.com/qedus/nds`
	`golang.org/x/net/context`
	`google.golang.org/appengine/datastore`
)

// Creates and configures a store that stores entities in Google AppEngines memcache and datastore.
// github.com/qedus/nds is used for strongly consistent automatic caching.
func NewGaeStore(ctx context.Context, kind string, idf IdFactory, vf VersionFactory) Store {
	getKey := func(ctx context.Context, id string) *datastore.Key {
		return datastore.NewKey(ctx, kind, id, 0, nil)
	}

	getMulti := func(ids []string) (vs []Version, err error) {
		count := len(ids)
		vs = make([]Version, count, count)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			vs[i] = vf()
			ks[i] = getKey(ctx, ids[i])
		}
		err = nds.GetMulti(ctx, ks, vs)
		return
	}

	putMulti := func(ids []string, vs []Version) (err error) {
		count := len(ids)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			ks[i] = getKey(ctx, ids[i])
		}
		_, err = nds.PutMulti(ctx, ks, vs)
		return
	}

	delMulti := func(ids []string) error {
		count := len(ids)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			ks[i] = getKey(ctx, ids[i])
		}
		return nds.DeleteMulti(ctx, ks)
	}

	isNonExtantError := func(err error) bool {
		return err == datastore.ErrNoSuchEntity
	}

	rit := func(tran Transaction) error {
		return nds.RunInTransaction(ctx, func(ctx context.Context)error{return tran()}, &datastore.TransactionOptions{XG:true})
	}

	return NewStore(getMulti, putMulti, delMulti, idf, vf, isNonExtantError,rit)
}<commit_msg>no need to pass in ctx<commit_after>package sus

import(
	`github.com/qedus/nds`
	`golang.org/x/net/context`
	`google.golang.org/appengine/datastore`
)

// Creates and configures a store that stores entities in Google AppEngines memcache and datastore.
// github.com/qedus/nds is used for strongly consistent automatic caching.
func NewGaeStore(kind string, idf IdFactory, vf VersionFactory) Store {
	var tranCtx context.Context

	getKey := func(ctx context.Context, id string) *datastore.Key {
		return datastore.NewKey(ctx, kind, id, 0, nil)
	}

	getMulti := func(ids []string) (vs []Version, err error) {
		count := len(ids)
		vs = make([]Version, count, count)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			vs[i] = vf()
			ks[i] = getKey(tranCtx, ids[i])
		}
		err = nds.GetMulti(tranCtx, ks, vs)
		return
	}

	putMulti := func(ids []string, vs []Version) (err error) {
		count := len(ids)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			ks[i] = getKey(tranCtx, ids[i])
		}
		_, err = nds.PutMulti(tranCtx, ks, vs)
		return
	}

	delMulti := func(ids []string) error {
		count := len(ids)
		ks := make([]*datastore.Key, count, count)
		for i := 0; i < count; i++ {
			ks[i] = getKey(tranCtx, ids[i])
		}
		return nds.DeleteMulti(tranCtx, ks)
	}

	isNonExtantError := func(err error) bool {
		return err == datastore.ErrNoSuchEntity
	}

	rit := func(tran Transaction) error {
		return nds.RunInTransaction(context.Background(), func(ctx context.Context)error{
			tranCtx = ctx
			return tran()
		}, &datastore.TransactionOptions{XG:true})
	}

	return NewStore(getMulti, putMulti, delMulti, idf, vf, isNonExtantError,rit)
}<|endoftext|>


<commit_before>package content

import "net/http"

// Item should only be embedded into content type structs.
type Item struct {
	ID        int    `json:"id"`
	Slug      string `json:"slug"`
	Timestamp int64  `json:"timestamp"`
	Updated   int64  `json:"updated"`
}

// Time partially implements the Sortable interface
func (i Item) Time() int64 {
	return i.Timestamp
}

// Touch partially implements the Sortable interface
func (i Item) Touch() int64 {
	return i.Updated
}

// ItemID partially implements the Sortable interface
func (i Item) ItemID() int {
	return i.ID
}

// SetSlug sets the item's slug for its URL
func (i *Item) SetSlug(slug string) {
	i.Slug = slug
}

// SetItemID sets the Item's ID field
func (i *Item) SetItemID(id int) {
	i.ID = id
}

// BeforeSave is a no-op to ensure structs which embed Item implement Hookable
func (i Item) BeforeSave(req *http.Request) error {
	return nil
}

// AfterSave is a no-op to ensure structs which embed Item implement Hookable
func (i Item) AfterSave(req *http.Request) error {
	return nil
}

// BeforeDelete is a no-op to ensure structs which embed Item implement Hookable
func (i Item) BeforeDelete(req *http.Request) error {
	return nil
}

// AfterDelete is a no-op to ensure structs which embed Item implement Hookable
func (i Item) AfterDelete(req *http.Request) error {
	return nil
}

// BeforeReject is a no-op to ensure structs which embed Item implement Hookable
func (i Item) BeforeReject(req *http.Request) error {
	return nil
}

// AfterReject is a no-op to ensure structs which embed Item implement Hookable
func (i Item) AfterReject(req *http.Request) error {
	return nil
}

// Sluggable makes a struct locatable by URL with it's own path
// As an Item implementing Sluggable, slugs may overlap. If this is an issue,
// make your content struct (or one which imbeds Item) implement Sluggable
// and it will override the slug created by Item's SetSlug with your struct's
type Sluggable interface {
	SetSlug(string)
}

// Identifiable enables a struct to have its ID set. Typically this is done
// to set an ID to -1 indicating it is new for DB inserts, since by default
// a newly initialized struct would have an ID of 0, the int zero-value, and
// BoltDB's starting key per bucket is 0, thus overwriting the first record.
type Identifiable interface {
	SetItemID(int)
}

// Hookable provides our user with an easy way to intercept or add functionality
// to the different lifecycles/events a struct may encounter. Item implements
// Hookable with no-ops so our user can override only whichever ones necessary.
type Hookable interface {
	BeforeSave(req *http.Request) error
	AfterSave(req *http.Request) error

	BeforeDelete(req *http.Request) error
	AfterDelete(req *http.Request) error

	BeforeApprove(req *http.Request) error
	AfterApprove(req *http.Request) error

	BeforeReject(req *http.Request) error
	AfterReject(req *http.Request) error
}
<commit_msg>testing revert Item to pointer in method reciever for hooks -- interface methods of emedded types may not be promoted to outer type if it is not same pointer/value type<commit_after>package content

import "net/http"

// Item should only be embedded into content type structs.
type Item struct {
	ID        int    `json:"id"`
	Slug      string `json:"slug"`
	Timestamp int64  `json:"timestamp"`
	Updated   int64  `json:"updated"`
}

// Time partially implements the Sortable interface
func (i Item) Time() int64 {
	return i.Timestamp
}

// Touch partially implements the Sortable interface
func (i Item) Touch() int64 {
	return i.Updated
}

// ItemID partially implements the Sortable interface
func (i Item) ItemID() int {
	return i.ID
}

// SetSlug sets the item's slug for its URL
func (i *Item) SetSlug(slug string) {
	i.Slug = slug
}

// SetItemID sets the Item's ID field
func (i *Item) SetItemID(id int) {
	i.ID = id
}

// BeforeSave is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) BeforeSave(req *http.Request) error {
	return nil
}

// AfterSave is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) AfterSave(req *http.Request) error {
	return nil
}

// BeforeDelete is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) BeforeDelete(req *http.Request) error {
	return nil
}

// AfterDelete is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) AfterDelete(req *http.Request) error {
	return nil
}

// BeforeReject is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) BeforeReject(req *http.Request) error {
	return nil
}

// AfterReject is a no-op to ensure structs which embed Item implement Hookable
func (i *Item) AfterReject(req *http.Request) error {
	return nil
}

// Sluggable makes a struct locatable by URL with it's own path
// As an Item implementing Sluggable, slugs may overlap. If this is an issue,
// make your content struct (or one which imbeds Item) implement Sluggable
// and it will override the slug created by Item's SetSlug with your struct's
type Sluggable interface {
	SetSlug(string)
}

// Identifiable enables a struct to have its ID set. Typically this is done
// to set an ID to -1 indicating it is new for DB inserts, since by default
// a newly initialized struct would have an ID of 0, the int zero-value, and
// BoltDB's starting key per bucket is 0, thus overwriting the first record.
type Identifiable interface {
	SetItemID(int)
}

// Hookable provides our user with an easy way to intercept or add functionality
// to the different lifecycles/events a struct may encounter. Item implements
// Hookable with no-ops so our user can override only whichever ones necessary.
type Hookable interface {
	BeforeSave(req *http.Request) error
	AfterSave(req *http.Request) error

	BeforeDelete(req *http.Request) error
	AfterDelete(req *http.Request) error

	BeforeApprove(req *http.Request) error
	AfterApprove(req *http.Request) error

	BeforeReject(req *http.Request) error
	AfterReject(req *http.Request) error
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/PuerkitoBio/goquery"
)

type item struct {
	id       string
	category string
	title    string
	url      string
}

func ScrapeMuusikoidenNet() {
	doc, err := goquery.NewDocument("https://muusikoiden.net/tori/?category=0")
	if err != nil {
		log.Fatal(err)
	}

	idFromURL, _ := regexp.Compile("/(\\d+)$")

	doc.Find("td.tori_title").Each(func(i int, titleContainer *goquery.Selection) {
		// TODO "Myydään" ääkköset are mangled
		category := titleContainer.Find("b").Text()
		link := titleContainer.Find("a")
		title := link.Text()
		url, _ := link.Attr("href")
		id := idFromURL.FindStringSubmatch(url)[1]
		item := item{id, category, title, url}
		fmt.Printf("%s\n", item)
	})
}

func main() {
	ScrapeMuusikoidenNet()
}
<commit_msg>Oh god<commit_after>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/PuerkitoBio/goquery"
)

type item struct {
	id       string
	category string
	title    string
	url      string
}

func ScrapeMuusikoidenNet() {
	doc, err := goquery.NewDocument("https://muusikoiden.net/tori/?category=0")
	if err != nil {
		log.Fatal(err)
	}

	idFromURL, _ := regexp.Compile("/(\\d+)$")

	elements := doc.Find("td.tori_title")

	items := make([]item, elements.Length())

	elements.Each(func(i int, titleContainer *goquery.Selection) {
		// TODO "Myydään" ääkköset are mangled
		category := titleContainer.Find("b").Text()
		link := titleContainer.Find("a")
		title := link.Text()
		url, _ := link.Attr("href")
		id := idFromURL.FindStringSubmatch(url)[1]
		item := item{id, category, title, url}
		items[i] = item
	})

	fmt.Printf("%s\n", items)
}

func main() {
	ScrapeMuusikoidenNet()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 election

import (
	"sync"
	"testing"
	"time"
)

type slowService struct {
	t  *testing.T
	on bool
	// We explicitly have no lock to prove that
	// Start and Stop are not called concurrently.
	changes chan<- bool
	wg      sync.WaitGroup
}

func (s *slowService) Validate(d, c Master) {
	// noop
}

func (s *slowService) Start() {
	if s.on {
		s.t.Errorf("started already on service")
	}
	defer s.wg.Add(1)
	time.Sleep(2 * time.Millisecond)
	s.on = true
	s.changes <- true
}

func (s *slowService) Stop() {
	if !s.on {
		s.t.Errorf("stopped already off service")
	}
	defer s.wg.Done()
	time.Sleep(2 * time.Millisecond)
	s.on = false
	s.changes <- false

}

func Test(t *testing.T) {
	m := NewFake()
	changes := make(chan bool, 1500)
	s := &slowService{t: t, changes: changes}
	go Notify(m, "", "me", s)

	done := make(chan struct{})
	go func() {
		for i := 0; i < 500; i++ {
			for _, key := range []string{"me", "notme", "alsonotme"} {
				m.ChangeMaster(Master(key))
			}
		}
		close(done)
	}()

	<-done
	time.Sleep(8 * time.Millisecond)
	ch := make(chan struct{})
	go func() {
		defer close(ch)
		s.wg.Wait()
	}()
	select {
	case <-time.After(1 * time.Second):
		t.Fatalf("timed out waiting for slow service to catch up")
	case <-ch: // expected
	}

	close(changes)

	changeList := []bool{}
	for {
		change, ok := <-changes
		if !ok {
			break
		}
		changeList = append(changeList, change)
	}

	if len(changeList) > 1000 {
		t.Errorf("unexpected number of changes: %v", len(changeList))
	}
}
<commit_msg>better fix for flaky test failures caused by writes to a closed chan<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 election

import (
	"testing"
	"time"
)

type slowService struct {
	t  *testing.T
	on bool
	// We explicitly have no lock to prove that
	// Start and Stop are not called concurrently.
	changes chan<- bool
	done    <-chan struct{}
}

func (s *slowService) Validate(d, c Master) {
	// noop
}

func (s *slowService) Start() {
	select {
	case <-s.done:
		return // avoid writing to closed changes chan
	default:
	}
	if s.on {
		s.t.Errorf("started already on service")
	}
	time.Sleep(2 * time.Millisecond)
	s.on = true
	s.changes <- true
}

func (s *slowService) Stop() {
	select {
	case <-s.done:
		return // avoid writing to closed changes chan
	default:
	}
	if !s.on {
		s.t.Errorf("stopped already off service")
	}
	time.Sleep(2 * time.Millisecond)
	s.on = false
	s.changes <- false
}

func Test(t *testing.T) {
	m := NewFake()
	changes := make(chan bool, 1500)
	done := make(chan struct{})
	s := &slowService{t: t, changes: changes, done: done}
	go Notify(m, "", "me", s)

	go func() {
		for i := 0; i < 500; i++ {
			for _, key := range []string{"me", "notme", "alsonotme"} {
				m.ChangeMaster(Master(key))
			}
		}
		close(done)
	}()

	<-done
	time.Sleep(8 * time.Millisecond)

	close(changes)

	changeList := []bool{}
	for {
		change, ok := <-changes
		if !ok {
			break
		}
		changeList = append(changeList, change)
	}

	if len(changeList) > 1000 {
		t.Errorf("unexpected number of changes: %v", len(changeList))
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// 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 client

import (
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
	"time"

	clientapi "github.com/cilium/cilium/api/v1/health/client"
	"github.com/cilium/cilium/api/v1/health/models"
	"github.com/cilium/cilium/pkg/health/defaults"

	runtime_client "github.com/go-openapi/runtime/client"
	"github.com/go-openapi/strfmt"
)

// Client is a client for cilium health
type Client struct {
	clientapi.CiliumHealth
}

func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
	if tr == nil {
		tr = &http.Transport{}
	}

	if proto == "unix" {
		// No need for compression in local communications.
		tr.DisableCompression = true
		tr.Dial = func(_, _ string) (net.Conn, error) {
			return net.Dial(proto, addr)
		}
	} else {
		tr.Proxy = http.ProxyFromEnvironment
		tr.Dial = (&net.Dialer{}).Dial
	}

	return tr
}

// NewDefaultClient creates a client with default parameters connecting to UNIX domain socket.
func NewDefaultClient() (*Client, error) {
	return NewClient("")
}

// NewClient creates a client for the given `host`.
func NewClient(host string) (*Client, error) {
	if host == "" {
		// Check if environment variable points to socket
		e := os.Getenv(defaults.SockPathEnv)
		if e == "" {
			// If unset, fall back to default value
			e = defaults.SockPath
		}
		host = "unix://" + e
	}
	tmp := strings.SplitN(host, "://", 2)
	if len(tmp) != 2 {
		return nil, fmt.Errorf("invalid host format '%s'", host)
	}

	switch tmp[0] {
	case "tcp":
		if _, err := url.Parse("tcp://" + tmp[1]); err != nil {
			return nil, err
		}
		host = "http://" + tmp[1]
	case "unix":
		host = tmp[1]
	}

	transport := configureTransport(nil, tmp[0], host)
	httpClient := &http.Client{Transport: transport}
	clientTrans := runtime_client.NewWithClient(tmp[1], clientapi.DefaultBasePath,
		clientapi.DefaultSchemes, httpClient)
	return &Client{*clientapi.New(clientTrans, strfmt.Default)}, nil
}

// Hint tries to improve the error message displayed to the user.
func Hint(err error) error {
	if err == nil {
		return err
	}
	e, _ := url.PathUnescape(err.Error())
	if strings.Contains(err.Error(), defaults.SockPath) {
		return fmt.Errorf("%s\nIs the agent running?", e)
	}
	return fmt.Errorf("%s", e)
}

func connectivityStatusHealthy(cs *models.ConnectivityStatus) bool {
	return cs != nil && cs.Status == ""
}

func formatConnectivityStatus(w io.Writer, cs *models.ConnectivityStatus, path, indent string) {
	status := cs.Status
	if connectivityStatusHealthy(cs) {
		latency := time.Duration(cs.Latency)
		status = fmt.Sprintf("OK, RTT=%s", latency)
	}
	fmt.Fprintf(w, "%s%s:\t%s\n", indent, path, status)
}

func formatPathStatus(w io.Writer, name string, cp *models.PathStatus, indent string, verbose bool) {
	if cp == nil {
		if verbose {
			fmt.Fprintf(w, "%s%s connectivity:\tnil\n", indent, name)
		}
		return
	}
	fmt.Fprintf(w, "%s%s connectivity to %s:\n", indent, name, cp.IP)
	indent = fmt.Sprintf("%s  ", indent)

	if cp.Icmp != nil {
		formatConnectivityStatus(w, cp.Icmp, "ICMP", indent)
	}
	if cp.HTTP != nil {
		formatConnectivityStatus(w, cp.HTTP, "HTTP via L3", indent)
	}
}

func pathIsHealthy(cp *models.PathStatus) bool {
	if cp == nil {
		return false
	}

	statuses := []*models.ConnectivityStatus{
		cp.Icmp,
		cp.HTTP,
	}
	for _, status := range statuses {
		if !connectivityStatusHealthy(status) {
			return false
		}
	}
	return true
}

func nodeIsHealthy(node *models.NodeStatus) bool {
	return pathIsHealthy(node.Host.PrimaryAddress) &&
		(node.Endpoint == nil || pathIsHealthy(node.Endpoint))
}

func nodeIsLocalhost(node *models.NodeStatus, self *models.SelfStatus) bool {
	return self != nil && node.Name == self.Name
}

func formatNodeStatus(w io.Writer, node *models.NodeStatus, printAll, succinct, verbose, localhost bool) {
	localStr := ""
	if localhost {
		localStr = " (localhost)"
	}
	if succinct {
		if printAll || !nodeIsHealthy(node) {
			fmt.Fprintf(w, "  %s%s\t%s\t%t\t%t\n", node.Name,
				localStr, node.Host.PrimaryAddress.IP,
				pathIsHealthy(node.Host.PrimaryAddress),
				pathIsHealthy(node.Endpoint))
		}
	} else {
		fmt.Fprintf(w, "  %s%s:\n", node.Name, localStr)
		formatPathStatus(w, "Host", node.Host.PrimaryAddress, "    ", verbose)
		if verbose && len(node.Host.SecondaryAddresses) > 0 {
			for _, addr := range node.Host.SecondaryAddresses {
				formatPathStatus(w, "Secondary", addr, "      ", verbose)
			}
		}
		formatPathStatus(w, "Endpoint", node.Endpoint, "    ", verbose)
	}
}

// FormatHealthStatusResponse writes a HealthStatusResponse as a string to the
// writer.
//
// 'printAll', if true, causes all nodes to be printed regardless of status
// 'succinct', if true, causes node health to be output as one line per node
// 'verbose', if true, overrides 'succinct' and prints all information
// 'maxLines', if nonzero, determines the maximum number of lines to print
func FormatHealthStatusResponse(w io.Writer, sr *models.HealthStatusResponse, printAll, succinct, verbose bool, maxLines int) {
	var (
		healthy   int
		localhost *models.NodeStatus
	)
	for _, node := range sr.Nodes {
		if nodeIsHealthy(node) {
			healthy++
		}
		if nodeIsLocalhost(node, sr.Local) {
			localhost = node
		}
	}
	if succinct {
		fmt.Fprintf(w, "Cluster health:\t%d/%d reachable\t(%s)\n",
			healthy, len(sr.Nodes), sr.Timestamp)
		if printAll || healthy < len(sr.Nodes) {
			fmt.Fprintf(w, "  Name\tIP\tReachable\tEndpoints reachable\n")
		}
	} else {
		fmt.Fprintf(w, "Probe time:\t%s\n", sr.Timestamp)
		fmt.Fprintf(w, "Nodes:\n")
	}

	if localhost != nil {
		formatNodeStatus(w, localhost, printAll, succinct, verbose, true)
		maxLines--
	}

	nodes := sr.Nodes
	sort.Slice(nodes, func(i, j int) bool {
		return strings.Compare(nodes[i].Name, nodes[j].Name) < 0
	})
	for n, node := range nodes {
		if maxLines > 0 && n > maxLines {
			break
		}
		if node == localhost {
			continue
		}
		formatNodeStatus(w, node, printAll, succinct, verbose, false)
	}
	if maxLines > 0 && len(sr.Nodes)-healthy > maxLines {
		fmt.Fprintf(w, "  ...")
	}
}

// GetAndFormatHealthStatus fetches the health status from the cilium-health
// daemon via the default channel and formats its output as a string to the
// writer.
//
// 'succinct', 'verbose' and 'maxLines' are handled the same as in
// FormatHealthStatusResponse().
func GetAndFormatHealthStatus(w io.Writer, succinct, verbose bool, maxLines int) {
	client, err := NewClient("")
	if err != nil {
		fmt.Fprintf(w, "Cluster health:\t\t\tClient error: %s\n", err)
		return
	}
	hr, err := client.Connectivity.GetStatus(nil)
	if err != nil {
		// The regular `cilium status` output will print the reason why.
		fmt.Fprintf(w, "Cluster health:\t\t\tWarning\tcilium-health daemon unreachable\n")
		return
	}
	FormatHealthStatusResponse(w, hr.Payload, verbose, succinct, verbose, maxLines)
}
<commit_msg>pkg/health/client: PathIsHealthy as a public function<commit_after>// Copyright 2017 Authors of Cilium
//
// 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 client

import (
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
	"time"

	clientapi "github.com/cilium/cilium/api/v1/health/client"
	"github.com/cilium/cilium/api/v1/health/models"
	"github.com/cilium/cilium/pkg/health/defaults"

	runtime_client "github.com/go-openapi/runtime/client"
	"github.com/go-openapi/strfmt"
)

// Client is a client for cilium health
type Client struct {
	clientapi.CiliumHealth
}

func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
	if tr == nil {
		tr = &http.Transport{}
	}

	if proto == "unix" {
		// No need for compression in local communications.
		tr.DisableCompression = true
		tr.Dial = func(_, _ string) (net.Conn, error) {
			return net.Dial(proto, addr)
		}
	} else {
		tr.Proxy = http.ProxyFromEnvironment
		tr.Dial = (&net.Dialer{}).Dial
	}

	return tr
}

// NewDefaultClient creates a client with default parameters connecting to UNIX domain socket.
func NewDefaultClient() (*Client, error) {
	return NewClient("")
}

// NewClient creates a client for the given `host`.
func NewClient(host string) (*Client, error) {
	if host == "" {
		// Check if environment variable points to socket
		e := os.Getenv(defaults.SockPathEnv)
		if e == "" {
			// If unset, fall back to default value
			e = defaults.SockPath
		}
		host = "unix://" + e
	}
	tmp := strings.SplitN(host, "://", 2)
	if len(tmp) != 2 {
		return nil, fmt.Errorf("invalid host format '%s'", host)
	}

	switch tmp[0] {
	case "tcp":
		if _, err := url.Parse("tcp://" + tmp[1]); err != nil {
			return nil, err
		}
		host = "http://" + tmp[1]
	case "unix":
		host = tmp[1]
	}

	transport := configureTransport(nil, tmp[0], host)
	httpClient := &http.Client{Transport: transport}
	clientTrans := runtime_client.NewWithClient(tmp[1], clientapi.DefaultBasePath,
		clientapi.DefaultSchemes, httpClient)
	return &Client{*clientapi.New(clientTrans, strfmt.Default)}, nil
}

// Hint tries to improve the error message displayed to the user.
func Hint(err error) error {
	if err == nil {
		return err
	}
	e, _ := url.PathUnescape(err.Error())
	if strings.Contains(err.Error(), defaults.SockPath) {
		return fmt.Errorf("%s\nIs the agent running?", e)
	}
	return fmt.Errorf("%s", e)
}

func connectivityStatusHealthy(cs *models.ConnectivityStatus) bool {
	return cs != nil && cs.Status == ""
}

func formatConnectivityStatus(w io.Writer, cs *models.ConnectivityStatus, path, indent string) {
	status := cs.Status
	if connectivityStatusHealthy(cs) {
		latency := time.Duration(cs.Latency)
		status = fmt.Sprintf("OK, RTT=%s", latency)
	}
	fmt.Fprintf(w, "%s%s:\t%s\n", indent, path, status)
}

func formatPathStatus(w io.Writer, name string, cp *models.PathStatus, indent string, verbose bool) {
	if cp == nil {
		if verbose {
			fmt.Fprintf(w, "%s%s connectivity:\tnil\n", indent, name)
		}
		return
	}
	fmt.Fprintf(w, "%s%s connectivity to %s:\n", indent, name, cp.IP)
	indent = fmt.Sprintf("%s  ", indent)

	if cp.Icmp != nil {
		formatConnectivityStatus(w, cp.Icmp, "ICMP", indent)
	}
	if cp.HTTP != nil {
		formatConnectivityStatus(w, cp.HTTP, "HTTP via L3", indent)
	}
}

// PathIsHealthy checks whether ICMP and TCP(HTTP) connectivity to the given
// path is available.
func PathIsHealthy(cp *models.PathStatus) bool {
	if cp == nil {
		return false
	}

	statuses := []*models.ConnectivityStatus{
		cp.Icmp,
		cp.HTTP,
	}
	for _, status := range statuses {
		if !connectivityStatusHealthy(status) {
			return false
		}
	}
	return true
}

func nodeIsHealthy(node *models.NodeStatus) bool {
	return PathIsHealthy(node.Host.PrimaryAddress) &&
		(node.Endpoint == nil || PathIsHealthy(node.Endpoint))
}

func nodeIsLocalhost(node *models.NodeStatus, self *models.SelfStatus) bool {
	return self != nil && node.Name == self.Name
}

func formatNodeStatus(w io.Writer, node *models.NodeStatus, printAll, succinct, verbose, localhost bool) {
	localStr := ""
	if localhost {
		localStr = " (localhost)"
	}
	if succinct {
		if printAll || !nodeIsHealthy(node) {
			fmt.Fprintf(w, "  %s%s\t%s\t%t\t%t\n", node.Name,
				localStr, node.Host.PrimaryAddress.IP,
				PathIsHealthy(node.Host.PrimaryAddress),
				PathIsHealthy(node.Endpoint))
		}
	} else {
		fmt.Fprintf(w, "  %s%s:\n", node.Name, localStr)
		formatPathStatus(w, "Host", node.Host.PrimaryAddress, "    ", verbose)
		if verbose && len(node.Host.SecondaryAddresses) > 0 {
			for _, addr := range node.Host.SecondaryAddresses {
				formatPathStatus(w, "Secondary", addr, "      ", verbose)
			}
		}
		formatPathStatus(w, "Endpoint", node.Endpoint, "    ", verbose)
	}
}

// FormatHealthStatusResponse writes a HealthStatusResponse as a string to the
// writer.
//
// 'printAll', if true, causes all nodes to be printed regardless of status
// 'succinct', if true, causes node health to be output as one line per node
// 'verbose', if true, overrides 'succinct' and prints all information
// 'maxLines', if nonzero, determines the maximum number of lines to print
func FormatHealthStatusResponse(w io.Writer, sr *models.HealthStatusResponse, printAll, succinct, verbose bool, maxLines int) {
	var (
		healthy   int
		localhost *models.NodeStatus
	)
	for _, node := range sr.Nodes {
		if nodeIsHealthy(node) {
			healthy++
		}
		if nodeIsLocalhost(node, sr.Local) {
			localhost = node
		}
	}
	if succinct {
		fmt.Fprintf(w, "Cluster health:\t%d/%d reachable\t(%s)\n",
			healthy, len(sr.Nodes), sr.Timestamp)
		if printAll || healthy < len(sr.Nodes) {
			fmt.Fprintf(w, "  Name\tIP\tReachable\tEndpoints reachable\n")
		}
	} else {
		fmt.Fprintf(w, "Probe time:\t%s\n", sr.Timestamp)
		fmt.Fprintf(w, "Nodes:\n")
	}

	if localhost != nil {
		formatNodeStatus(w, localhost, printAll, succinct, verbose, true)
		maxLines--
	}

	nodes := sr.Nodes
	sort.Slice(nodes, func(i, j int) bool {
		return strings.Compare(nodes[i].Name, nodes[j].Name) < 0
	})
	for n, node := range nodes {
		if maxLines > 0 && n > maxLines {
			break
		}
		if node == localhost {
			continue
		}
		formatNodeStatus(w, node, printAll, succinct, verbose, false)
	}
	if maxLines > 0 && len(sr.Nodes)-healthy > maxLines {
		fmt.Fprintf(w, "  ...")
	}
}

// GetAndFormatHealthStatus fetches the health status from the cilium-health
// daemon via the default channel and formats its output as a string to the
// writer.
//
// 'succinct', 'verbose' and 'maxLines' are handled the same as in
// FormatHealthStatusResponse().
func GetAndFormatHealthStatus(w io.Writer, succinct, verbose bool, maxLines int) {
	client, err := NewClient("")
	if err != nil {
		fmt.Fprintf(w, "Cluster health:\t\t\tClient error: %s\n", err)
		return
	}
	hr, err := client.Connectivity.GetStatus(nil)
	if err != nil {
		// The regular `cilium status` output will print the reason why.
		fmt.Fprintf(w, "Cluster health:\t\t\tWarning\tcilium-health daemon unreachable\n")
		return
	}
	FormatHealthStatusResponse(w, hr.Payload, verbose, succinct, verbose, maxLines)
}
<|endoftext|>