<commit_before>/*
© Copyright IBM Corporation 2018, 2019

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 keystore contains code to create and update keystores
package keystore

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/ibm-messaging/mq-container/internal/command"
)

// KeyStore describes information about a keystore file
type KeyStore struct {
	Filename     string
	Password     string
	keyStoreType string
	command      string
}

// NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command
func NewJKSKeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "jks",
		command:      "/opt/mqm/bin/runmqckm",
	}
}

// NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command
func NewCMSKeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "cms",
		command:      "/opt/mqm/bin/runmqakm",
	}
}

// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
func NewPKCS12KeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "p12",
		command:      "/opt/mqm/bin/runmqakm",
	}
}

// Create a key store, if it doesn't already exist
func (ks *KeyStore) Create() error {
	_, err := os.Stat(ks.Filename)
	if err == nil {
		// Keystore already exists so we should refresh it by deleting it.
		extension := filepath.Ext(ks.Filename)
		if ks.keyStoreType == "cms" {
			// Only delete these when we are refreshing the kdb keystore
			stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
			rdbFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".rdb"
			crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
			err = os.Remove(stashFile)
			if err != nil {
				return err
			}
			err = os.Remove(rdbFile)
			if err != nil {
				return err
			}
			err = os.Remove(crlFile)
			if err != nil {
				return err
			}
		}
		err = os.Remove(ks.Filename)
		if err != nil {
			return err
		}
	} else if !os.IsNotExist(err) {
		// If the keystore exists but cannot be accessed then return the error
		return err
	}

	// Create the keystore now we're sure it doesn't exist
	out, _, err := command.Run(ks.command, "-keydb", "-create", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash")
	if err != nil {
		return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
	}

	mqmUID, mqmGID, err := command.LookupMQM()
	if err != nil {
		return err
	}
	err = os.Chown(ks.Filename, mqmUID, mqmGID)
	if err != nil {
		return err
	}
	return nil
}

// CreateStash creates a key stash, if it doesn't already exist
func (ks *KeyStore) CreateStash() error {
	extension := filepath.Ext(ks.Filename)
	stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
	_, err := os.Stat(stashFile)
	if err != nil {
		if os.IsNotExist(err) {
			out, _, err := command.Run(ks.command, "-keydb", "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
			if err != nil {
				return fmt.Errorf("error running \"%v -keydb -stashpw\": %v %s", ks.command, err, out)
			}
		}
		return err
	}
	mqmUID, mqmGID, err := command.LookupMQM()
	if err != nil {
		return err
	}
	err = os.Chown(stashFile, mqmUID, mqmGID)
	if err != nil {
		return err
	}
	return nil
}

// Import imports a certificate file in the keystore
func (ks *KeyStore) Import(inputFile, password string) error {
	out, _, err := command.Run(ks.command, "-cert", "-import", "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -import\": %v %s", ks.command, err, out)
	}
	return nil
}

// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn string) error {
	out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
	}
	return nil
}

// Add adds a CA certificate to the keystore
func (ks *KeyStore) Add(inputFile, label string) error {
	out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
	}
	return nil
}

// Add adds a CA certificate to the keystore
func (ks *KeyStore) AddNoLabel(inputFile string) error {
	out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
	}
	return nil
}

// GetCertificateLabels returns the labels of all certificates in the key store
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
	out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
	if err != nil {
		return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
	}
	scanner := bufio.NewScanner(strings.NewReader(out))
	var labels []string
	for scanner.Scan() {
		s := scanner.Text()
		if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") {
			s := strings.TrimLeft(s, "-*")
			labels = append(labels, strings.TrimSpace(s))
		}
	}
	err = scanner.Err()
	if err != nil {
		return nil, err
	}
	return labels, nil
}

// RenameCertificate renames the specified certificate
func (ks *KeyStore) RenameCertificate(from, to string) error {
	out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
	}
	return nil
}

// ListCertificates Lists all certificates in the keystore
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
	out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
	if err != nil {
		return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
	}
	scanner := bufio.NewScanner(strings.NewReader(out))
	var labels []string
	for scanner.Scan() {
		s := scanner.Text()
		if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
			s := strings.TrimLeft(s, "-*!")
			labels = append(labels, strings.TrimSpace(s))
		}
	}
	err = scanner.Err()
	if err != nil {
		return nil, err
	}
	return labels, nil
}
<commit_msg>use capicmd for renaming certs<commit_after>/*
© Copyright IBM Corporation 2018, 2019

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 keystore contains code to create and update keystores
package keystore

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/ibm-messaging/mq-container/internal/command"
)

// KeyStore describes information about a keystore file
type KeyStore struct {
	Filename     string
	Password     string
	keyStoreType string
	command      string
}

// NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command
func NewJKSKeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "jks",
		command:      "/opt/mqm/bin/runmqckm",
	}
}

// NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command
func NewCMSKeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "cms",
		command:      "/opt/mqm/bin/runmqakm",
	}
}

// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
func NewPKCS12KeyStore(filename, password string) *KeyStore {
	return &KeyStore{
		Filename:     filename,
		Password:     password,
		keyStoreType: "p12",
		command:      "/opt/mqm/bin/runmqakm",
	}
}

// Create a key store, if it doesn't already exist
func (ks *KeyStore) Create() error {
	_, err := os.Stat(ks.Filename)
	if err == nil {
		// Keystore already exists so we should refresh it by deleting it.
		extension := filepath.Ext(ks.Filename)
		if ks.keyStoreType == "cms" {
			// Only delete these when we are refreshing the kdb keystore
			stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
			rdbFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".rdb"
			crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
			err = os.Remove(stashFile)
			if err != nil {
				return err
			}
			err = os.Remove(rdbFile)
			if err != nil {
				return err
			}
			err = os.Remove(crlFile)
			if err != nil {
				return err
			}
		}
		err = os.Remove(ks.Filename)
		if err != nil {
			return err
		}
	} else if !os.IsNotExist(err) {
		// If the keystore exists but cannot be accessed then return the error
		return err
	}

	// Create the keystore now we're sure it doesn't exist
	out, _, err := command.Run(ks.command, "-keydb", "-create", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash")
	if err != nil {
		return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
	}

	mqmUID, mqmGID, err := command.LookupMQM()
	if err != nil {
		return err
	}
	err = os.Chown(ks.Filename, mqmUID, mqmGID)
	if err != nil {
		return err
	}
	return nil
}

// CreateStash creates a key stash, if it doesn't already exist
func (ks *KeyStore) CreateStash() error {
	extension := filepath.Ext(ks.Filename)
	stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
	_, err := os.Stat(stashFile)
	if err != nil {
		if os.IsNotExist(err) {
			out, _, err := command.Run(ks.command, "-keydb", "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
			if err != nil {
				return fmt.Errorf("error running \"%v -keydb -stashpw\": %v %s", ks.command, err, out)
			}
		}
		return err
	}
	mqmUID, mqmGID, err := command.LookupMQM()
	if err != nil {
		return err
	}
	err = os.Chown(stashFile, mqmUID, mqmGID)
	if err != nil {
		return err
	}
	return nil
}

// Import imports a certificate file in the keystore
func (ks *KeyStore) Import(inputFile, password string) error {
	out, _, err := command.Run(ks.command, "-cert", "-import", "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -import\": %v %s", ks.command, err, out)
	}
	return nil
}

// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn string) error {
	out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
	}
	return nil
}

// Add adds a CA certificate to the keystore
func (ks *KeyStore) Add(inputFile, label string) error {
	out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
	}
	return nil
}

// Add adds a CA certificate to the keystore
func (ks *KeyStore) AddNoLabel(inputFile string) error {
	out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile)
	if err != nil {
		return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
	}
	return nil
}

// GetCertificateLabels returns the labels of all certificates in the key store
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
	out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
	if err != nil {
		return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
	}
	scanner := bufio.NewScanner(strings.NewReader(out))
	var labels []string
	for scanner.Scan() {
		s := scanner.Text()
		if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") {
			s := strings.TrimLeft(s, "-*")
			labels = append(labels, strings.TrimSpace(s))
		}
	}
	err = scanner.Err()
	if err != nil {
		return nil, err
	}
	return labels, nil
}

// RenameCertificate renames the specified certificate
func (ks *KeyStore) RenameCertificate(from, to string) error {
	if ks.command == "/opt/mqm/bin/runmqakm" {
		// runmqakm can't handle certs with ' in them so just use capicmd
		cmd := exec.Command("/opt/mqm/gskit8/bin/gsk8capicmd_64", "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
		cmd.Env = append(os.Environ(), "LD_LIBRARY_PATH=/opt/mqm/gskit8/lib64/:/opt/mqm/gskit8/lib")
		out, _, err := command.RunCmd(cmd)
		if err != nil {
			return fmt.Errorf("error running \"%v -cert -rename\": %v %s", "/opt/mqm/gskit8/bin/gsk8capicmd_64", err, out)
		}
	} else {
		out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
		if err != nil {
			return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
		}
	}

	return nil
}

// ListCertificates Lists all certificates in the keystore
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
	out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
	if err != nil {
		return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
	}
	scanner := bufio.NewScanner(strings.NewReader(out))
	var labels []string
	for scanner.Scan() {
		s := scanner.Text()
		if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
			s := strings.TrimLeft(s, "-*!")
			labels = append(labels, strings.TrimSpace(s))
		}
	}
	err = scanner.Err()
	if err != nil {
		return nil, err
	}
	return labels, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"bytes"
	"fmt"
	"log"
	"path"
	"regexp"
	"strings"

	"github.com/russross/blackfriday"
)

const (
	DEFAULT_NUM_COLUMNS = 80
	DEFAULT_TAB_SIZE    = 4
)

const (
	FLAG_NO_TOC = 1 << iota
	FLAG_NO_RULES
	FLAG_PASCAL
)

type list struct {
	index int
}

type header struct {
	text     []byte
	level    int
	children []*header
}

type vimDoc struct {
	filename string
	title    string
	desc     string
	cols     int
	tabs     int
	flags    int
	tocPos   int
	lists    []*list
	rootHead *header
	lastHead *header
}

func VimDocRenderer(filename, desc string, cols, tabs, flags int) blackfriday.Renderer {
	filename = path.Base(filename)
	title := filename

	if index := strings.LastIndex(filename, "."); index > -1 {
		title = filename[:index]
		if flags&FLAG_PASCAL == 0 {
			title = strings.ToLower(title)
		}
	}

	return &vimDoc{
		filename: filename,
		title:    title,
		desc:     desc,
		cols:     cols,
		tabs:     tabs,
		flags:    flags,
		tocPos:   -1}
}

func (v *vimDoc) pushl() {
	v.lists = append(v.lists, &list{1})
}

func (v *vimDoc) popl() {
	if len(v.lists) == 0 {
		log.Fatal("error: invalid list operation")
	}

	v.lists = v.lists[:len(v.lists)-1]
}

func (v *vimDoc) getl() *list {
	if len(v.lists) == 0 {
		log.Fatal("error: invalid list operation")
	}

	return v.lists[len(v.lists)-1]
}

func (v *vimDoc) fixupCode(input []byte) []byte {
	r := regexp.MustCompile(`(?m)^\s*([<>])$`)
	return r.ReplaceAll(input, []byte("$1"))
}

func (v *vimDoc) fixupHeader(header []byte) []byte {
	return bytes.ToUpper(header)
}

func (v *vimDoc) buildTag(header []byte) []byte {
	if v.flags&FLAG_PASCAL == 0 {
		header = bytes.ToLower(header)
		header = bytes.Replace(header, []byte{' '}, []byte{'_'}, -1)
	} else {
		header = bytes.ToTitle(header)
		header = bytes.Replace(header, []byte{' '}, []byte{}, -1)
	}

	return []byte(fmt.Sprintf("%s-%s", v.title, header))
}

func (v *vimDoc) writeStraddle(out *bytes.Buffer, left, right []byte, trim int) {
	padding := v.cols - (len(left) + len(right)) + trim
	if padding <= 0 {
		padding = 1
	}

	out.Write(left)
	out.WriteString(strings.Repeat(" ", padding))
	out.Write(right)
	out.WriteString("\n")
}

func (v *vimDoc) writeRule(out *bytes.Buffer, repeat string) {
	out.WriteString(strings.Repeat(repeat, v.cols))
	out.WriteString("\n")
}

func (v *vimDoc) writeToc(out *bytes.Buffer, h *header, depth int) {
	title := fmt.Sprintf("%s%s", strings.Repeat(" ", depth*v.tabs), h.text)
	link := fmt.Sprintf("|%s|", v.buildTag(h.text))
	v.writeStraddle(out, []byte(title), []byte(link), 2)

	for _, c := range h.children {
		v.writeToc(out, c, depth+1)
	}
}

func (v *vimDoc) format(out *bytes.Buffer, text string, trim int) {
	lines := strings.Split(text, "\n")

	for index, line := range lines {
		width := v.tabs
		if width >= trim && index == 0 {
			width -= trim
		}

		if len(line) > 0 {
			out.WriteString(strings.Repeat(" ", width))
			out.WriteString(line)
			out.WriteString("\n")
		}
	}
}

// Block-level callbacks
func (v *vimDoc) BlockCode(out *bytes.Buffer, text []byte, lang string) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) BlockQuote(out *bytes.Buffer, text []byte) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) BlockHtml(out *bytes.Buffer, text []byte) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) Header(out *bytes.Buffer, text func() bool, level int, id string) {
	initPos := out.Len()

	if v.flags&FLAG_NO_RULES == 0 {
		switch level {
		case 1:
			v.writeRule(out, "=")
		case 2:
			v.writeRule(out, "-")
		}
	}

	headerPos := out.Len()

	if !text() {
		out.Truncate(initPos)
		return
	}

	if v.tocPos == -1 && v.rootHead != nil {
		v.tocPos = initPos
	}

	var value []byte
	value = append(value, out.Bytes()[headerPos:]...)
	header := &header{value, level, nil}

	if v.lastHead == nil {
		if header.level != 1 {
			log.Println("warning: top-level header in document is not a level 1 header")
		}

		v.rootHead = header
		v.lastHead = header
	} else {
		if v.rootHead.level >= header.level {
			log.Println("warning: found header of higher or equal level to the root header")
		}

		if header.level <= v.lastHead.level {
			v.lastHead = header
		} else {
			v.lastHead.children = append(v.lastHead.children, header)
		}
	}

	out.Truncate(headerPos)
	tag := fmt.Sprintf("*%s*", v.buildTag(header.text))
	v.writeStraddle(out, v.fixupHeader(header.text), []byte(tag), 2)
	out.WriteString("\n")
}

func (v *vimDoc) HRule(out *bytes.Buffer) {
	v.writeRule(out, "-")
}

func (v *vimDoc) List(out *bytes.Buffer, text func() bool, flags int) {
	v.pushl()
	text()
	v.popl()
}

func (v *vimDoc) ListItem(out *bytes.Buffer, text []byte, flags int) {
	marker := out.Len()

	list := v.getl()
	if flags&blackfriday.LIST_TYPE_ORDERED == blackfriday.LIST_TYPE_ORDERED {
		out.WriteString(fmt.Sprintf("%d. ", list.index))
		list.index++
	} else {
		out.WriteString("* ")
	}

	v.format(out, string(text), out.Len()-marker)

	if flags&blackfriday.LIST_ITEM_END_OF_LIST != 0 {
		out.WriteString("\n")
	}
}

func (*vimDoc) Paragraph(out *bytes.Buffer, text func() bool) {
	marker := out.Len()

	if !text() {
		out.Truncate(marker)
		return
	}

	out.WriteString("\n\n")
}

func (*vimDoc) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
	// unimplemented
	log.Println("warning: Table is a stub")
}

func (*vimDoc) TableRow(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: TableRow is a stub")
}

func (*vimDoc) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {
	// unimplemented
	log.Println("warning: TableHeaderCell is a stub")
}

func (*vimDoc) TableCell(out *bytes.Buffer, text []byte, flags int) {
	// unimplemented
	log.Println("warning: TableCell is a stub")
}

func (*vimDoc) Footnotes(out *bytes.Buffer, text func() bool) {
	// unimplemented
	log.Println("warning: Footnotes is a stub")
}

func (*vimDoc) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
	// unimplemented
	log.Println("warning: FootnoteItem is a stub")
}

func (*vimDoc) TitleBlock(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: TitleBlock is a stub")
}

// Span-level callbacks
func (*vimDoc) AutoLink(out *bytes.Buffer, link []byte, kind int) {
	out.Write(link)
}

func (*vimDoc) CodeSpan(out *bytes.Buffer, text []byte) {
	r := regexp.MustCompile(`\s`)

	// vim does not correctly highlight space-delimited words in code spans
	if !r.Match(text) {
		out.WriteString("`")
		out.Write(text)
		out.WriteString("`")
	}
}

func (*vimDoc) DoubleEmphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) Emphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
	// cannot view images in vim
}

func (*vimDoc) LineBreak(out *bytes.Buffer) {
	out.WriteString("\n")
}

func (*vimDoc) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
	out.WriteString(fmt.Sprintf("%s (%s)", content, link))
}

func (*vimDoc) RawHtmlTag(out *bytes.Buffer, tag []byte) {
	// unimplemented
	log.Println("warning: StrikeThrough is a stub")
}

func (*vimDoc) TripleEmphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) StrikeThrough(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: StrikeThrough is a stub")
}

func (*vimDoc) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
	// unimplemented
	log.Println("warning: FootnoteRef is a stub")
}

// Low-level callbacks
func (v *vimDoc) Entity(out *bytes.Buffer, entity []byte) {
	out.Write(entity)
}

func (v *vimDoc) NormalText(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

// Header and footer
func (v *vimDoc) DocumentHeader(out *bytes.Buffer) {
	if len(v.desc) > 0 {
		v.writeStraddle(out, []byte(v.filename), []byte(v.desc), 0)
	} else {
		out.WriteString(v.filename)
		out.WriteString("\n")
	}
}

func (v *vimDoc) DocumentFooter(out *bytes.Buffer) {
	var temp bytes.Buffer

	if v.tocPos > 0 && v.flags&FLAG_NO_TOC == 0 {
		temp.Write(out.Bytes()[:v.tocPos])
		v.writeToc(&temp, v.rootHead, 0)
		temp.WriteString("\n")
		temp.Write(out.Bytes()[v.tocPos:])
	} else {
		temp.ReadFrom(out)
	}

	out.Reset()
	out.Write(v.fixupCode(temp.Bytes()))
}

func (v *vimDoc) GetFlags() int {
	return v.flags
}
<commit_msg>Improvements<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"bytes"
	"fmt"
	"log"
	"path"
	"regexp"
	"strings"

	"github.com/russross/blackfriday"
)

const (
	DEFAULT_NUM_COLUMNS = 80
	DEFAULT_TAB_SIZE    = 4
)

const (
	FLAG_NO_TOC = 1 << iota
	FLAG_NO_RULES
	FLAG_PASCAL
)

type list struct {
	index int
}

type header struct {
	text     []byte
	level    int
	children []*header
}

type vimDoc struct {
	filename string
	title    string
	desc     string
	cols     int
	tabs     int
	flags    int
	tocPos   int
	lists    []*list
	rootHead *header
	lastHead *header
}

func VimDocRenderer(filename, desc string, cols, tabs, flags int) blackfriday.Renderer {
	filename = path.Base(filename)
	title := filename

	if index := strings.LastIndex(filename, "."); index > -1 {
		title = filename[:index]
		if flags&FLAG_PASCAL == 0 {
			title = strings.ToLower(title)
		}
	}

	return &vimDoc{
		filename: filename,
		title:    title,
		desc:     desc,
		cols:     cols,
		tabs:     tabs,
		flags:    flags,
		tocPos:   -1}
}

func (v *vimDoc) pushl() {
	v.lists = append(v.lists, &list{1})
}

func (v *vimDoc) popl() {
	if len(v.lists) == 0 {
		log.Fatal("error: invalid list operation")
	}

	v.lists = v.lists[:len(v.lists)-1]
}

func (v *vimDoc) getl() *list {
	if len(v.lists) == 0 {
		log.Fatal("error: invalid list operation")
	}

	return v.lists[len(v.lists)-1]
}

func (v *vimDoc) fixupCode(input []byte) []byte {
	r := regexp.MustCompile(`(?m)^\s*([<>])$`)
	return r.ReplaceAll(input, []byte("$1"))
}

func (v *vimDoc) fixupHeader(header []byte) []byte {
	return bytes.ToUpper(header)
}

func (v *vimDoc) buildTag(header []byte) []byte {
	if v.flags&FLAG_PASCAL == 0 {
		header = bytes.ToLower(header)
		header = bytes.Replace(header, []byte{' '}, []byte{'_'}, -1)
	} else {
		header = bytes.Title(header)
		header = bytes.Replace(header, []byte{' '}, []byte{}, -1)
	}

	return []byte(fmt.Sprintf("%s-%s", v.title, header))
}

func (v *vimDoc) writeStraddle(out *bytes.Buffer, left, right []byte, trim int) {
	padding := v.cols - (len(left) + len(right)) + trim
	if padding <= 0 {
		padding = 1
	}

	out.Write(left)
	out.WriteString(strings.Repeat(" ", padding))
	out.Write(right)
	out.WriteString("\n")
}

func (v *vimDoc) writeRule(out *bytes.Buffer, repeat string) {
	out.WriteString(strings.Repeat(repeat, v.cols))
	out.WriteString("\n")
}

func (v *vimDoc) writeToc(out *bytes.Buffer, h *header, depth int) {
	title := fmt.Sprintf("%s%s", strings.Repeat(" ", depth*v.tabs), h.text)
	link := fmt.Sprintf("|%s|", v.buildTag(h.text))
	v.writeStraddle(out, []byte(title), []byte(link), 2)

	for _, c := range h.children {
		v.writeToc(out, c, depth+1)
	}
}

func (v *vimDoc) format(out *bytes.Buffer, text string, trim int) {
	lines := strings.Split(text, "\n")

	for index, line := range lines {
		width := v.tabs
		if width >= trim && index == 0 {
			width -= trim
		}

		if len(line) > 0 {
			out.WriteString(strings.Repeat(" ", width))
			out.WriteString(line)
			out.WriteString("\n")
		}
	}
}

// Block-level callbacks
func (v *vimDoc) BlockCode(out *bytes.Buffer, text []byte, lang string) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) BlockQuote(out *bytes.Buffer, text []byte) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) BlockHtml(out *bytes.Buffer, text []byte) {
	out.WriteString(">\n")
	v.format(out, string(text), 0)
	out.WriteString("<\n\n")
}

func (v *vimDoc) Header(out *bytes.Buffer, text func() bool, level int, id string) {
	initPos := out.Len()

	if v.flags&FLAG_NO_RULES == 0 {
		switch level {
		case 1:
			v.writeRule(out, "=")
		case 2:
			v.writeRule(out, "-")
		}
	}

	headerPos := out.Len()

	if !text() {
		out.Truncate(initPos)
		return
	}

	if v.tocPos == -1 && v.rootHead != nil {
		v.tocPos = initPos
	}

	var value []byte
	value = append(value, out.Bytes()[headerPos:]...)
	header := &header{value, level, nil}

	if v.lastHead == nil {
		if header.level != 1 {
			log.Println("warning: top-level header in document is not a level 1 header")
		}

		v.rootHead = header
		v.lastHead = header
	} else {
		if v.rootHead.level >= header.level {
			log.Println("warning: found header of higher or equal level to the root header")
		}

		if header.level <= v.lastHead.level {
			v.lastHead = header
		} else {
			v.lastHead.children = append(v.lastHead.children, header)
		}
	}

	out.Truncate(headerPos)
	tag := fmt.Sprintf("*%s*", v.buildTag(header.text))
	v.writeStraddle(out, v.fixupHeader(header.text), []byte(tag), 2)
	out.WriteString("\n")
}

func (v *vimDoc) HRule(out *bytes.Buffer) {
	v.writeRule(out, "-")
}

func (v *vimDoc) List(out *bytes.Buffer, text func() bool, flags int) {
	v.pushl()
	text()
	v.popl()
}

func (v *vimDoc) ListItem(out *bytes.Buffer, text []byte, flags int) {
	marker := out.Len()

	list := v.getl()
	if flags&blackfriday.LIST_TYPE_ORDERED == blackfriday.LIST_TYPE_ORDERED {
		out.WriteString(fmt.Sprintf("%d. ", list.index))
		list.index++
	} else {
		out.WriteString("* ")
	}

	v.format(out, string(text), out.Len()-marker)

	if flags&blackfriday.LIST_ITEM_END_OF_LIST != 0 {
		out.WriteString("\n")
	}
}

func (*vimDoc) Paragraph(out *bytes.Buffer, text func() bool) {
	marker := out.Len()

	if !text() {
		out.Truncate(marker)
		return
	}

	out.WriteString("\n\n")
}

func (*vimDoc) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
	// unimplemented
	log.Println("warning: Table is a stub")
}

func (*vimDoc) TableRow(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: TableRow is a stub")
}

func (*vimDoc) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {
	// unimplemented
	log.Println("warning: TableHeaderCell is a stub")
}

func (*vimDoc) TableCell(out *bytes.Buffer, text []byte, flags int) {
	// unimplemented
	log.Println("warning: TableCell is a stub")
}

func (*vimDoc) Footnotes(out *bytes.Buffer, text func() bool) {
	// unimplemented
	log.Println("warning: Footnotes is a stub")
}

func (*vimDoc) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
	// unimplemented
	log.Println("warning: FootnoteItem is a stub")
}

func (*vimDoc) TitleBlock(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: TitleBlock is a stub")
}

// Span-level callbacks
func (*vimDoc) AutoLink(out *bytes.Buffer, link []byte, kind int) {
	out.Write(link)
}

func (*vimDoc) CodeSpan(out *bytes.Buffer, text []byte) {
	r := regexp.MustCompile(`\s`)

	// vim does not correctly highlight space-delimited words in code spans
	if !r.Match(text) {
		out.WriteString("`")
		out.Write(text)
		out.WriteString("`")
	}
}

func (*vimDoc) DoubleEmphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) Emphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
	// cannot view images in vim
}

func (*vimDoc) LineBreak(out *bytes.Buffer) {
	out.WriteString("\n")
}

func (*vimDoc) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
	out.WriteString(fmt.Sprintf("%s (%s)", content, link))
}

func (*vimDoc) RawHtmlTag(out *bytes.Buffer, tag []byte) {
	// unimplemented
	log.Println("warning: StrikeThrough is a stub")
}

func (*vimDoc) TripleEmphasis(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

func (*vimDoc) StrikeThrough(out *bytes.Buffer, text []byte) {
	// unimplemented
	log.Println("warning: StrikeThrough is a stub")
}

func (*vimDoc) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
	// unimplemented
	log.Println("warning: FootnoteRef is a stub")
}

// Low-level callbacks
func (v *vimDoc) Entity(out *bytes.Buffer, entity []byte) {
	out.Write(entity)
}

func (v *vimDoc) NormalText(out *bytes.Buffer, text []byte) {
	out.Write(text)
}

// Header and footer
func (v *vimDoc) DocumentHeader(out *bytes.Buffer) {
	if len(v.desc) > 0 {
		v.writeStraddle(out, []byte(v.filename), []byte(v.desc), 0)
	} else {
		out.WriteString(v.filename)
		out.WriteString("\n")
	}

	out.WriteString("\n")
}

func (v *vimDoc) DocumentFooter(out *bytes.Buffer) {
	var temp bytes.Buffer

	if v.tocPos > 0 && v.flags&FLAG_NO_TOC == 0 {
		temp.Write(out.Bytes()[:v.tocPos])
		v.writeToc(&temp, v.rootHead, 0)
		temp.WriteString("\n")
		temp.Write(out.Bytes()[v.tocPos:])
	} else {
		temp.ReadFrom(out)
	}

	out.Reset()
	out.Write(v.fixupCode(temp.Bytes()))
}

func (v *vimDoc) GetFlags() int {
	return v.flags
}
<|endoftext|>


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

package arm64

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
)

// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed.
func TestLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("Skip in short mode")
	}
	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "testlarge")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// generate a very large function
	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
	gen(buf)

	tmpfile := filepath.Join(dir, "x.s")
	err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
	if err != nil {
		t.Fatalf("can't write output: %v\n", err)
	}

	// build generated file
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = []string{"GOARCH=arm64", "GOOS=linux"}
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Errorf("Build failed: %v, output: %s", err, out)
	}
}

// gen generates a very large program, with a very far conditional branch.
func gen(buf *bytes.Buffer) {
	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
	fmt.Fprintln(buf, "TBZ $5, R0, label")
	fmt.Fprintln(buf, "CBZ R0, label")
	fmt.Fprintln(buf, "BEQ label")
	for i := 0; i < 1<<19; i++ {
		fmt.Fprintln(buf, "MOVD R0, R1")
	}
	fmt.Fprintln(buf, "label:")
	fmt.Fprintln(buf, "RET")
}

// Issue 20348.
func TestNoRet(t *testing.T) {
	dir, err := ioutil.TempDir("", "testnoret")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "x.s")
	if err := ioutil.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = []string{"GOARCH=arm64", "GOOS=linux"}
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("%v\n%s", err, out)
	}
}
<commit_msg>cmd/internal/obj/arm64: do not clear environment in TestLarge and TestNoRet<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package arm64

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
)

// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed.
func TestLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("Skip in short mode")
	}
	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "testlarge")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// generate a very large function
	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
	gen(buf)

	tmpfile := filepath.Join(dir, "x.s")
	err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
	if err != nil {
		t.Fatalf("can't write output: %v\n", err)
	}

	// build generated file
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Errorf("Build failed: %v, output: %s", err, out)
	}
}

// gen generates a very large program, with a very far conditional branch.
func gen(buf *bytes.Buffer) {
	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
	fmt.Fprintln(buf, "TBZ $5, R0, label")
	fmt.Fprintln(buf, "CBZ R0, label")
	fmt.Fprintln(buf, "BEQ label")
	for i := 0; i < 1<<19; i++ {
		fmt.Fprintln(buf, "MOVD R0, R1")
	}
	fmt.Fprintln(buf, "label:")
	fmt.Fprintln(buf, "RET")
}

// Issue 20348.
func TestNoRet(t *testing.T) {
	dir, err := ioutil.TempDir("", "testnoret")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "x.s")
	if err := ioutil.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("%v\n%s", err, out)
	}
}
<|endoftext|>


<commit_before>// Author: Conor Raftery
// Date: 2017-09-27

// Adapted from: https://tour.golang.org/welcome/1

package main

import "fmt"

func main() {
	fmt.Println("こんにちは 世界")
}<commit_msg>Task 1 Complete<commit_after>// Author: Conor Raftery
// Date: 2017-09-27

// Adapted from: https://tour.golang.org/welcome/1

package main

import "fmt"

func main() {
	fmt.Println("こんにちは 世界")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/url"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func Test_extractTagNames(t *testing.T) {
	expext := []string{"tag1", "tag2", "tag2"}

	values := url.Values{}

	values.Add("tags[]", "tag1")
	values.Add("tags[]", "tag2")
	values.Add("tags[]", "tag2")

	assert.Equal(t, expext, extractTagNames(values))

	values.Del("tags[]")
	values.Set("tags", "tag1,tag2,tag2")

	assert.Equal(t, expext, extractTagNames(values))
}

func Test_buildCreateLogResponse(t *testing.T) {
	now := time.Now()
	tags := []Tag{Tag{1, "tag1"}, Tag{1, "tag2"}}

	logRecord := LogRecord{
		Id:          123,
		Application: Application{1, "TestApp"},
		Level:       5,
		CreatedAt:   &now,
		Message:     "Lorem ipsum",
		Tags:        tags,
	}

	res, _ := buildCreateLogResponse(&logRecord)

	parsed := LogRecordResponse{}
	json.Unmarshal(res, &parsed)

	assert.Equal(t, 123, parsed.Id)
	assert.Equal(t, "TestApp", parsed.Application)
	assert.Equal(t, 5, parsed.Level)
	assert.EqualValues(t, &now, parsed.CreatedAt)
	assert.Equal(t, "Lorem ipsum", parsed.Message)
	assert.Equal(t, []string{"tag1", "tag2"}, parsed.Tags)
}

func Test_checkCreateLogParams(t *testing.T) {
	assert.Nil(t, checkCreateLogParams("Lorem", "1", []string{"tag1", "tag2"}))
	assert.Nil(t, checkCreateLogParams("Lorem", "1", []string{}))
	assert.Nil(t, checkCreateLogParams("Lorem", "5", []string{}))

	assert.Error(t, checkCreateLogParams("", "1", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "0", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "6", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "1", []string{""}))
}

func Test_buildGetLogsResponse(t *testing.T) {
	now := time.Now()
	tags := []Tag{Tag{1, "tag1"}, Tag{1, "tag2"}}

	logRecords := []LogRecord{
		LogRecord{
			Id:          123,
			Application: Application{1, "TestApp"},
			Level:       5,
			CreatedAt:   &now,
			Message:     "Lorem ipsum",
			Tags:        tags,
		},
		LogRecord{
			Id:          456,
			Application: Application{1, "TestApp"},
			Level:       4,
			CreatedAt:   &now,
			Message:     "Dolor sit amet",
			Tags:        tags,
		},
	}

	res, _ := buildGetLogsResponse(&logRecords)

	parsed := LogRecordsResponse{}
	json.Unmarshal(res, &parsed)

	assert.Len(t, parsed, 2)

	for i, response := range parsed {
		assert.Equal(t, logRecords[i].Id, response.Id)
		assert.Equal(t, logRecords[i].Application.Name, response.Application)
		assert.Equal(t, logRecords[i].Level, response.Level)
		assert.EqualValues(t, &now, response.CreatedAt)
		assert.Equal(t, logRecords[i].Message, response.Message)
		assert.Equal(t, []string{"tag1", "tag2"}, response.Tags)
	}
}

func Test_checkGetLogParams(t *testing.T) {
	assert.Nil(t, checkGetLogParams("1", []string{"tag1", "tag2"}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("5", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("1", []string{}, "2014-01-02 03:04:05", "2014-03-04 05:06:07", "1"))

	assert.Error(t, checkGetLogParams("", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("0", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("6", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{""}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-022", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-044", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-04", "a"))
}
<commit_msg>Debug info for travis<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func Test_extractTagNames(t *testing.T) {
	expext := []string{"tag1", "tag2", "tag2"}

	values := url.Values{}

	values.Add("tags[]", "tag1")
	values.Add("tags[]", "tag2")
	values.Add("tags[]", "tag2")

	assert.Equal(t, expext, extractTagNames(values))

	values.Del("tags[]")
	values.Set("tags", "tag1,tag2,tag2")

	assert.Equal(t, expext, extractTagNames(values))
}

func Test_buildCreateLogResponse(t *testing.T) {
	now := time.Now()
	tags := []Tag{Tag{1, "tag1"}, Tag{1, "tag2"}}

	logRecord := LogRecord{
		Id:          123,
		Application: Application{1, "TestApp"},
		Level:       5,
		CreatedAt:   &now,
		Message:     "Lorem ipsum",
		Tags:        tags,
	}

	res, _ := buildCreateLogResponse(&logRecord)

	parsed := LogRecordResponse{}
	json.Unmarshal(res, &parsed)

	fmt.Println(res)
	fmt.Println(logRecord.CreatedAt)
	fmt.Println(parsed.CreatedAt)

	assert.Equal(t, 123, parsed.Id)
	assert.Equal(t, "TestApp", parsed.Application)
	assert.Equal(t, 5, parsed.Level)
	assert.EqualValues(t, &now, parsed.CreatedAt)
	assert.Equal(t, "Lorem ipsum", parsed.Message)
	assert.Equal(t, []string{"tag1", "tag2"}, parsed.Tags)
}

func Test_checkCreateLogParams(t *testing.T) {
	assert.Nil(t, checkCreateLogParams("Lorem", "1", []string{"tag1", "tag2"}))
	assert.Nil(t, checkCreateLogParams("Lorem", "1", []string{}))
	assert.Nil(t, checkCreateLogParams("Lorem", "5", []string{}))

	assert.Error(t, checkCreateLogParams("", "1", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "0", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "6", []string{}))
	assert.Error(t, checkCreateLogParams("Lorem", "1", []string{""}))
}

func Test_buildGetLogsResponse(t *testing.T) {
	now := time.Now()
	tags := []Tag{Tag{1, "tag1"}, Tag{1, "tag2"}}

	logRecords := []LogRecord{
		LogRecord{
			Id:          123,
			Application: Application{1, "TestApp"},
			Level:       5,
			CreatedAt:   &now,
			Message:     "Lorem ipsum",
			Tags:        tags,
		},
		LogRecord{
			Id:          456,
			Application: Application{1, "TestApp"},
			Level:       4,
			CreatedAt:   &now,
			Message:     "Dolor sit amet",
			Tags:        tags,
		},
	}

	res, _ := buildGetLogsResponse(&logRecords)

	parsed := LogRecordsResponse{}
	json.Unmarshal(res, &parsed)

	assert.Len(t, parsed, 2)

	for i, response := range parsed {
		assert.Equal(t, logRecords[i].Id, response.Id)
		assert.Equal(t, logRecords[i].Application.Name, response.Application)
		assert.Equal(t, logRecords[i].Level, response.Level)
		assert.EqualValues(t, &now, response.CreatedAt)
		assert.Equal(t, logRecords[i].Message, response.Message)
		assert.Equal(t, []string{"tag1", "tag2"}, response.Tags)
	}
}

func Test_checkGetLogParams(t *testing.T) {
	assert.Nil(t, checkGetLogParams("1", []string{"tag1", "tag2"}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("5", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Nil(t, checkGetLogParams("1", []string{}, "2014-01-02 03:04:05", "2014-03-04 05:06:07", "1"))

	assert.Error(t, checkGetLogParams("", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("0", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("6", []string{}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{""}, "2014-01-02", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-022", "2014-03-04", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-044", "1"))
	assert.Error(t, checkGetLogParams("1", []string{}, "2014-01-02", "2014-03-04", "a"))
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/gonet2/libs/nsq-logger"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import (
	"utils"
)

var (
	wg sync.WaitGroup
	// server close signal
	die = make(chan bool)
)

// handle unix signals
func sig_handler() {
	defer utils.PrintPanicStack()
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGTERM)

	for {
		msg := <-ch
		switch msg {
		case syscall.SIGTERM: // 关闭agent
			close(die)
			log.Info("sigterm received")
			log.Info("waiting for agents close, please wait...")
			wg.Wait()
			log.Info("agent shutdown.")
			os.Exit(0)
		}
	}
}
<commit_msg>use chan struct{}<commit_after>package main

import (
	log "github.com/gonet2/libs/nsq-logger"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import (
	"utils"
)

var (
	wg sync.WaitGroup
	// server close signal
	die = make(chan struct{})
)

// handle unix signals
func sig_handler() {
	defer utils.PrintPanicStack()
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGTERM)

	for {
		msg := <-ch
		switch msg {
		case syscall.SIGTERM: // 关闭agent
			close(die)
			log.Info("sigterm received")
			log.Info("waiting for agents close, please wait...")
			wg.Wait()
			log.Info("agent shutdown.")
			os.Exit(0)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
)

var fileRoute = regexp.MustCompile("^/files/([^/\r\n]+)\r?$")
var filesRoute = regexp.MustCompile("^/files/?$")
var dataStore *DataStore

func init() {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}

	dataDir := path.Join(wd, "tus_data")
	if err := os.MkdirAll(dataDir, 0777); err != nil {
		panic(err)
	}
	dataStore = NewDataStore(dataDir)
}

func serveHttp() error {
	http.HandleFunc("/", route)

	addr := ":1080"
	log.Printf("serving clients at %s", addr)

	return http.ListenAndServe(addr, nil)
}

func route(w http.ResponseWriter, r *http.Request) {
	log.Printf("request: %s %s", r.Method, r.URL.RequestURI())

	w.Header().Set("Server", "tusd")
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, x-requested-with, content-type, accept, Content-Range, Content-Disposition")
	w.Header().Add("Access-Control-Expose-Headers", "Location, Range")

	if r.Method == "OPTIONS" {
		reply(w, http.StatusOK, "")
		return
	}

	if r.Method == "POST" && filesRoute.Match([]byte(r.URL.Path)) {
		postFiles(w, r)
	} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
		id := match[1]
		switch r.Method {
		case "HEAD":
			headFile(w, r, id)
		case "GET":
			getFile(w, r, id)
		case "POST":
			putFile(w, r, id)
		case "PUT":
			putFile(w, r, id)
		default:
			reply(w, http.StatusMethodNotAllowed, "Invalid http method")
		}
	} else {
		reply(w, http.StatusNotFound, "No matching route")
	}
}

func reply(w http.ResponseWriter, code int, message string) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
}

func postFiles(w http.ResponseWriter, r *http.Request) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		log.Print("FOO")
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	if contentRange.Size == -1 {
		log.Print("FOO2")
		reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.")
		return
	}

	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	id := uid()
	if err := dataStore.CreateFile(id, contentRange.Size, contentType); err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	if contentRange.End != -1 {
		if err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body); err != nil {
			// @TODO: Could be a 404 as well
			reply(w, http.StatusInternalServerError, err.Error())
			return
		}
	}

	w.Header().Set("Location", "/files/"+id)
	setFileRangeHeader(w, id)
	w.WriteHeader(http.StatusCreated)
}

func headFile(w http.ResponseWriter, r *http.Request, fileId string) {
	setFileRangeHeader(w, fileId)
}

func getFile(w http.ResponseWriter, r *http.Request, fileId string) {
	data, size, err := dataStore.ReadFile(fileId)
	if err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}
	defer data.Close()

	setFileRangeHeader(w, fileId)
	w.Header().Set("Content-Length", strconv.FormatInt(size, 10))

	if _, err := io.CopyN(w, data, size); err != nil {
		log.Printf("getFile: CopyN failed with: %s", err.Error())
		return
	}
}

func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
	var start int64 = 0
	var end int64 = 0

	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		contentLength := r.Header.Get("Content-Length")
		end, err = strconv.ParseInt(contentLength, 10, 64)
		if err != nil {
			reply(w, http.StatusBadRequest, "Invalid content length provided")
		}

		// we are zero-indexed
		end = end - 1

		// @TODO: Make sure contentLength matches the content length of the initial
		// POST request
	} else {

		// @TODO: Make sure contentRange.Size matches file size

		start = contentRange.Start
		end = contentRange.End
	}

	// @TODO: Check that file exists

	if err := dataStore.WriteFileChunk(fileId, start, end, r.Body); err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	setFileRangeHeader(w, fileId)
}

func setFileRangeHeader(w http.ResponseWriter, fileId string) {
	chunks, err := dataStore.GetFileChunks(fileId)
	if err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	rangeHeader := ""
	for i, chunk := range chunks {
		rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
		if i+1 < len(chunks) {
			rangeHeader += ","
		}
	}

	if rangeHeader != "" {
		w.Header().Set("Range", "bytes="+rangeHeader)
	}
}
<commit_msg>Comment on route change<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
)

// fileRoute matches /files/<id>. Go seems to use \r to terminate header
// values, so to ease bash scripting, the route ignores a trailing \r in the
// route. Better ideas are welcome.
var fileRoute = regexp.MustCompile("^/files/([^/\r\n]+)\r?$")

var filesRoute = regexp.MustCompile("^/files/?$")
var dataStore *DataStore

func init() {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}

	dataDir := path.Join(wd, "tus_data")
	if err := os.MkdirAll(dataDir, 0777); err != nil {
		panic(err)
	}
	dataStore = NewDataStore(dataDir)
}

func serveHttp() error {
	http.HandleFunc("/", route)

	addr := ":1080"
	log.Printf("serving clients at %s", addr)

	return http.ListenAndServe(addr, nil)
}

func route(w http.ResponseWriter, r *http.Request) {
	log.Printf("request: %s %s", r.Method, r.URL.RequestURI())

	w.Header().Set("Server", "tusd")
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, x-requested-with, content-type, accept, Content-Range, Content-Disposition")
	w.Header().Add("Access-Control-Expose-Headers", "Location, Range")

	if r.Method == "OPTIONS" {
		reply(w, http.StatusOK, "")
		return
	}

	if r.Method == "POST" && filesRoute.Match([]byte(r.URL.Path)) {
		postFiles(w, r)
	} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
		id := match[1]
		switch r.Method {
		case "HEAD":
			headFile(w, r, id)
		case "GET":
			getFile(w, r, id)
		case "POST":
			putFile(w, r, id)
		case "PUT":
			putFile(w, r, id)
		default:
			reply(w, http.StatusMethodNotAllowed, "Invalid http method")
		}
	} else {
		reply(w, http.StatusNotFound, "No matching route")
	}
}

func reply(w http.ResponseWriter, code int, message string) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
}

func postFiles(w http.ResponseWriter, r *http.Request) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		log.Print("FOO")
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	if contentRange.Size == -1 {
		log.Print("FOO2")
		reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.")
		return
	}

	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	id := uid()
	if err := dataStore.CreateFile(id, contentRange.Size, contentType); err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	if contentRange.End != -1 {
		if err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body); err != nil {
			// @TODO: Could be a 404 as well
			reply(w, http.StatusInternalServerError, err.Error())
			return
		}
	}

	w.Header().Set("Location", "/files/"+id)
	setFileRangeHeader(w, id)
	w.WriteHeader(http.StatusCreated)
}

func headFile(w http.ResponseWriter, r *http.Request, fileId string) {
	setFileRangeHeader(w, fileId)
}

func getFile(w http.ResponseWriter, r *http.Request, fileId string) {
	data, size, err := dataStore.ReadFile(fileId)
	if err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}
	defer data.Close()

	setFileRangeHeader(w, fileId)
	w.Header().Set("Content-Length", strconv.FormatInt(size, 10))

	if _, err := io.CopyN(w, data, size); err != nil {
		log.Printf("getFile: CopyN failed with: %s", err.Error())
		return
	}
}

func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
	var start int64 = 0
	var end int64 = 0

	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		contentLength := r.Header.Get("Content-Length")
		end, err = strconv.ParseInt(contentLength, 10, 64)
		if err != nil {
			reply(w, http.StatusBadRequest, "Invalid content length provided")
		}

		// we are zero-indexed
		end = end - 1

		// @TODO: Make sure contentLength matches the content length of the initial
		// POST request
	} else {

		// @TODO: Make sure contentRange.Size matches file size

		start = contentRange.Start
		end = contentRange.End
	}

	// @TODO: Check that file exists

	if err := dataStore.WriteFileChunk(fileId, start, end, r.Body); err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	setFileRangeHeader(w, fileId)
}

func setFileRangeHeader(w http.ResponseWriter, fileId string) {
	chunks, err := dataStore.GetFileChunks(fileId)
	if err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	rangeHeader := ""
	for i, chunk := range chunks {
		rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
		if i+1 < len(chunks) {
			rangeHeader += ","
		}
	}

	if rangeHeader != "" {
		w.Header().Set("Range", "bytes="+rangeHeader)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/rwcarlsen/goexif/exif"
)

func exitOnError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main() {
	if len(os.Args) > 1 {
		for _, path := range os.Args[1:] {
			f, err := os.Open(path)
			exitOnError(err)
			defer f.Close()

			ex, err := exif.Decode(f)
			exitOnError(err)

			date, err := ex.DateTime()
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error processing:", path, err)
				continue
			}

			dir, file := filepath.Split(path)
			ext := filepath.Ext(file)

			newname := fmt.Sprintf("%04d_%02d_%02d_%02d_%02d_%02d%s",
				date.Year(), int(date.Month()), date.Day(),
				date.Hour(), date.Minute(), date.Second(), ext)

			newpath := filepath.Join(dir, newname)

			fmt.Printf("%s -> %s\n", path, newpath)
			os.Rename(path, newpath)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error processing:", path, err)
				continue
			}
		}
	}
}
<commit_msg>handle duplicate filenames by adding postfix index<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/rwcarlsen/goexif/exif"
)

func exitOnError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func fileExists(filename string) bool {
	_, err := os.Stat(filename)
	return !os.IsNotExist(err)
}

func makeNewname(date time.Time, ext string, count int) string {
	return fmt.Sprintf("%04d_%02d_%02d_%02d_%02d_%02d_%02d%s",
		date.Year(), int(date.Month()), date.Day(),
		date.Hour(), date.Minute(), date.Second(), count, ext)
}

func main() {
	if len(os.Args) > 1 {
		for _, path := range os.Args[1:] {
			f, err := os.Open(path)
			exitOnError(err)
			defer f.Close()

			ex, err := exif.Decode(f)
			exitOnError(err)

			date, err := ex.DateTime()
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error processing:", path, err)
				continue
			}

			dir, file := filepath.Split(path)
			ext := filepath.Ext(file)

			count := 0
			for {

				newname := makeNewname(date, ext, count)
				newpath := filepath.Join(dir, newname)

				if fileExists(newpath) {
					count++
				} else {
					fmt.Printf("%s -> %s\n", path, newpath)
					os.Rename(path, newpath)
					if err != nil {
						fmt.Fprintln(os.Stderr, "Error processing:", path, err)
					}
					break
				}
			}

		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 audio

import (
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten"
)

type mixingStream struct {
	sampleRate   int
	writtenBytes int
	frames       int
	players      map[*Player]struct{}
	sync.RWMutex
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

const (
	channelNum     = 2
	bytesPerSample = 2
	bitsPerSample  = bytesPerSample * 8

	// TODO: This assumes that channelNum is a power of 2.
	mask = ^(channelNum*bytesPerSample - 1)
)

func (s *mixingStream) Read(b []byte) (int, error) {
	s.Lock()
	defer s.Unlock()

	bytesPerFrame := s.sampleRate * bytesPerSample * channelNum / ebiten.FPS
	x := s.frames*bytesPerFrame + len(b)
	if x <= s.writtenBytes {
		return 0, nil
	}

	if len(s.players) == 0 {
		l := min(len(b), x-s.writtenBytes)
		l &= mask
		copy(b, make([]byte, l))
		s.writtenBytes += l
		return l, nil
	}
	closed := []*Player{}
	l := len(b)
	for p := range s.players {
		_, err := p.readToBuffer(l)
		if err == io.EOF {
			closed = append(closed, p)
		} else if err != nil {
			return 0, err
		}
		l = min(p.bufferLength(), l)
	}
	l &= mask
	b16s := [][]int16{}
	for p := range s.players {
		b16s = append(b16s, p.bufferToInt16(l))
	}
	for i := 0; i < l/2; i++ {
		x := 0
		for _, b16 := range b16s {
			x += int(b16[i])
		}
		if x > (1<<15)-1 {
			x = (1 << 15) - 1
		}
		if x < -(1 << 15) {
			x = -(1 << 15)
		}
		b[2*i] = byte(x)
		b[2*i+1] = byte(x >> 8)
	}
	for p := range s.players {
		p.proceed(l)
	}
	for _, p := range closed {
		delete(s.players, p)
	}
	s.writtenBytes += l
	return l, nil
}

func (s *mixingStream) update() {
	s.Lock()
	defer s.Unlock()
	s.frames++
}

func (s *mixingStream) newPlayer(src ReadSeekCloser) (*Player, error) {
	s.Lock()
	defer s.Unlock()
	p := &Player{
		stream: s,
		src:    src,
		buf:    []byte{},
		volume: 1,
	}
	// Get the current position of the source.
	pos, err := p.src.Seek(0, 1)
	if err != nil {
		return nil, err
	}
	p.pos = pos
	runtime.SetFinalizer(p, (*Player).Close)
	return p, nil
}

func (s *mixingStream) closePlayer(player *Player) error {
	s.Lock()
	defer s.Unlock()
	runtime.SetFinalizer(player, nil)
	return player.src.Close()
}

func (s *mixingStream) addPlayer(player *Player) {
	s.Lock()
	defer s.Unlock()
	s.players[player] = struct{}{}
}

func (s *mixingStream) removePlayer(player *Player) {
	s.Lock()
	defer s.Unlock()
	delete(s.players, player)
}

func (s *mixingStream) hasPlayer(player *Player) bool {
	s.RLock()
	defer s.RUnlock()
	_, ok := s.players[player]
	return ok
}

func (s *mixingStream) seekPlayer(player *Player, offset time.Duration) error {
	s.Lock()
	defer s.Unlock()
	o := int64(offset) * bytesPerSample * channelNum * int64(s.sampleRate) / int64(time.Second)
	o &= mask
	return player.seek(o)
}

func (s *mixingStream) playerCurrent(player *Player) time.Duration {
	s.RLock()
	defer s.RUnlock()
	sample := player.pos / bytesPerSample / channelNum
	return time.Duration(sample) * time.Second / time.Duration(s.sampleRate)
}

// TODO: Enable to specify the format like Mono8?

type Context struct {
	sampleRate int
	stream     *mixingStream
	errorCh    chan error
}

func NewContext(sampleRate int) (*Context, error) {
	// TODO: Panic if one context exists.
	c := &Context{
		sampleRate: sampleRate,
		errorCh:    make(chan error),
	}
	c.stream = &mixingStream{
		sampleRate: sampleRate,
		players:    map[*Player]struct{}{},
	}
	p, err := newPlayer(c.stream, c.sampleRate)
	if err != nil {
		return nil, err
	}
	go func() {
		// TODO: Is it OK to close asap?
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				c.errorCh <- err
				return
			}
			time.Sleep(1 * time.Millisecond)
		}
	}()
	return c, nil
}

// Update proceeds the inner (logical) time of the context by 1/60 second.
// This is expected to be called in the game's updating function (sync mode)
// or an independent goroutine with timers (unsync mode).
// In sync mode, the game logical time syncs the audio logical time and
// you will find audio stops when the game stops e.g. when the window is deactivated.
// In unsync mode, the audio never stops even when the game stops.
func (c *Context) Update() error {
	select {
	case err := <-c.errorCh:
		return err
	default:
	}
	c.stream.update()
	return nil
}

// SampleRate returns the sample rate.
// All audio source must have the same sample rate.
func (c *Context) SampleRate() int {
	return c.sampleRate
}

// ReadSeekCloser is an io.ReadSeeker and io.Closer.
type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}

// Player is an audio player which has one stream.
type Player struct {
	stream *mixingStream
	src    ReadSeekCloser
	buf    []byte
	pos    int64
	volume float64
}

// NewPlayer creates a new player with the given data to the given channel.
// The given data is queued to the end of the buffer.
// This may not be played immediately when data already exists in the buffer.
//
// src's format must be linear PCM (16bits, 2 channel stereo, little endian)
// without a header (e.g. RIFF header).
func (c *Context) NewPlayer(src ReadSeekCloser) (*Player, error) {
	return c.stream.newPlayer(src)
}

func (p *Player) Close() error {
	return p.stream.closePlayer(p)
}

func (p *Player) readToBuffer(length int) (int, error) {
	bb := make([]byte, length)
	n, err := p.src.Read(bb)
	if 0 < n {
		p.buf = append(p.buf, bb[:n]...)
	}
	return n, err
}

func (p *Player) bufferToInt16(lengthInBytes int) []int16 {
	r := make([]int16, lengthInBytes/2)
	for i := 0; i < lengthInBytes/2; i++ {
		r[i] = int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8)
		r[i] = int16(float64(r[i]) * p.volume)
	}
	return r
}

func (p *Player) proceed(length int) {
	p.buf = p.buf[length:]
	p.pos += int64(length)
}

func (p *Player) bufferLength() int {
	return len(p.buf)
}

func (p *Player) Play() error {
	p.stream.addPlayer(p)
	return nil
}

func (p *Player) IsPlaying() bool {
	return p.stream.hasPlayer(p)
}

func (p *Player) Rewind() error {
	return p.Seek(0)
}

func (p *Player) Seek(offset time.Duration) error {
	return p.stream.seekPlayer(p, offset)
}

func (p *Player) seek(offset int64) error {
	p.buf = []byte{}
	pos, err := p.src.Seek(offset, 0)
	if err != nil {
		return err
	}
	p.pos = pos
	return nil
}

func (p *Player) Pause() error {
	p.stream.removePlayer(p)
	return nil
}

func (p *Player) Current() time.Duration {
	return p.stream.playerCurrent(p)
}

func (p *Player) Volume() float64 {
	return p.volume
}

// SetVolume sets the volume.
// volume must be in between 0 and 1. This function panics otherwise.
func (p *Player) SetVolume(volume float64) {
	// The condition must be true when volume is NaN.
	if !(0 <= volume && volume <= 1) {
		panic("audio: volume must be in between 0 and 1")
	}
	p.volume = volume
}

// TODO: Panning
<commit_msg>audio: Refactoring: Remove context.sampleRate<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 audio

import (
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten"
)

type mixingStream struct {
	sampleRate   int
	writtenBytes int
	frames       int
	players      map[*Player]struct{}
	sync.RWMutex
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

const (
	channelNum     = 2
	bytesPerSample = 2
	bitsPerSample  = bytesPerSample * 8

	// TODO: This assumes that channelNum is a power of 2.
	mask = ^(channelNum*bytesPerSample - 1)
)

func (s *mixingStream) Read(b []byte) (int, error) {
	s.Lock()
	defer s.Unlock()

	bytesPerFrame := s.sampleRate * bytesPerSample * channelNum / ebiten.FPS
	x := s.frames*bytesPerFrame + len(b)
	if x <= s.writtenBytes {
		return 0, nil
	}

	if len(s.players) == 0 {
		l := min(len(b), x-s.writtenBytes)
		l &= mask
		copy(b, make([]byte, l))
		s.writtenBytes += l
		return l, nil
	}
	closed := []*Player{}
	l := len(b)
	for p := range s.players {
		_, err := p.readToBuffer(l)
		if err == io.EOF {
			closed = append(closed, p)
		} else if err != nil {
			return 0, err
		}
		l = min(p.bufferLength(), l)
	}
	l &= mask
	b16s := [][]int16{}
	for p := range s.players {
		b16s = append(b16s, p.bufferToInt16(l))
	}
	for i := 0; i < l/2; i++ {
		x := 0
		for _, b16 := range b16s {
			x += int(b16[i])
		}
		if x > (1<<15)-1 {
			x = (1 << 15) - 1
		}
		if x < -(1 << 15) {
			x = -(1 << 15)
		}
		b[2*i] = byte(x)
		b[2*i+1] = byte(x >> 8)
	}
	for p := range s.players {
		p.proceed(l)
	}
	for _, p := range closed {
		delete(s.players, p)
	}
	s.writtenBytes += l
	return l, nil
}

func (s *mixingStream) update() {
	s.Lock()
	defer s.Unlock()
	s.frames++
}

func (s *mixingStream) newPlayer(src ReadSeekCloser) (*Player, error) {
	s.Lock()
	defer s.Unlock()
	p := &Player{
		stream: s,
		src:    src,
		buf:    []byte{},
		volume: 1,
	}
	// Get the current position of the source.
	pos, err := p.src.Seek(0, 1)
	if err != nil {
		return nil, err
	}
	p.pos = pos
	runtime.SetFinalizer(p, (*Player).Close)
	return p, nil
}

func (s *mixingStream) closePlayer(player *Player) error {
	s.Lock()
	defer s.Unlock()
	runtime.SetFinalizer(player, nil)
	return player.src.Close()
}

func (s *mixingStream) addPlayer(player *Player) {
	s.Lock()
	defer s.Unlock()
	s.players[player] = struct{}{}
}

func (s *mixingStream) removePlayer(player *Player) {
	s.Lock()
	defer s.Unlock()
	delete(s.players, player)
}

func (s *mixingStream) hasPlayer(player *Player) bool {
	s.RLock()
	defer s.RUnlock()
	_, ok := s.players[player]
	return ok
}

func (s *mixingStream) seekPlayer(player *Player, offset time.Duration) error {
	s.Lock()
	defer s.Unlock()
	o := int64(offset) * bytesPerSample * channelNum * int64(s.sampleRate) / int64(time.Second)
	o &= mask
	return player.seek(o)
}

func (s *mixingStream) playerCurrent(player *Player) time.Duration {
	s.RLock()
	defer s.RUnlock()
	sample := player.pos / bytesPerSample / channelNum
	return time.Duration(sample) * time.Second / time.Duration(s.sampleRate)
}

// TODO: Enable to specify the format like Mono8?

type Context struct {
	stream  *mixingStream
	errorCh chan error
}

func NewContext(sampleRate int) (*Context, error) {
	// TODO: Panic if one context exists.
	c := &Context{
		errorCh: make(chan error),
	}
	c.stream = &mixingStream{
		sampleRate: sampleRate,
		players:    map[*Player]struct{}{},
	}
	// TODO: Rename this other than player
	p, err := newPlayer(c.stream, sampleRate)
	if err != nil {
		return nil, err
	}
	go func() {
		// TODO: Is it OK to close asap?
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				c.errorCh <- err
				return
			}
			time.Sleep(1 * time.Millisecond)
		}
	}()
	return c, nil
}

// Update proceeds the inner (logical) time of the context by 1/60 second.
// This is expected to be called in the game's updating function (sync mode)
// or an independent goroutine with timers (unsync mode).
// In sync mode, the game logical time syncs the audio logical time and
// you will find audio stops when the game stops e.g. when the window is deactivated.
// In unsync mode, the audio never stops even when the game stops.
func (c *Context) Update() error {
	select {
	case err := <-c.errorCh:
		return err
	default:
	}
	c.stream.update()
	return nil
}

// SampleRate returns the sample rate.
// All audio source must have the same sample rate.
func (c *Context) SampleRate() int {
	return c.stream.sampleRate
}

// ReadSeekCloser is an io.ReadSeeker and io.Closer.
type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}

// Player is an audio player which has one stream.
type Player struct {
	stream *mixingStream
	src    ReadSeekCloser
	buf    []byte
	pos    int64
	volume float64
}

// NewPlayer creates a new player with the given data to the given channel.
// The given data is queued to the end of the buffer.
// This may not be played immediately when data already exists in the buffer.
//
// src's format must be linear PCM (16bits, 2 channel stereo, little endian)
// without a header (e.g. RIFF header).
func (c *Context) NewPlayer(src ReadSeekCloser) (*Player, error) {
	return c.stream.newPlayer(src)
}

func (p *Player) Close() error {
	return p.stream.closePlayer(p)
}

func (p *Player) readToBuffer(length int) (int, error) {
	bb := make([]byte, length)
	n, err := p.src.Read(bb)
	if 0 < n {
		p.buf = append(p.buf, bb[:n]...)
	}
	return n, err
}

func (p *Player) bufferToInt16(lengthInBytes int) []int16 {
	r := make([]int16, lengthInBytes/2)
	for i := 0; i < lengthInBytes/2; i++ {
		r[i] = int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8)
		r[i] = int16(float64(r[i]) * p.volume)
	}
	return r
}

func (p *Player) proceed(length int) {
	p.buf = p.buf[length:]
	p.pos += int64(length)
}

func (p *Player) bufferLength() int {
	return len(p.buf)
}

func (p *Player) Play() error {
	p.stream.addPlayer(p)
	return nil
}

func (p *Player) IsPlaying() bool {
	return p.stream.hasPlayer(p)
}

func (p *Player) Rewind() error {
	return p.Seek(0)
}

func (p *Player) Seek(offset time.Duration) error {
	return p.stream.seekPlayer(p, offset)
}

func (p *Player) seek(offset int64) error {
	p.buf = []byte{}
	pos, err := p.src.Seek(offset, 0)
	if err != nil {
		return err
	}
	p.pos = pos
	return nil
}

func (p *Player) Pause() error {
	p.stream.removePlayer(p)
	return nil
}

func (p *Player) Current() time.Duration {
	return p.stream.playerCurrent(p)
}

func (p *Player) Volume() float64 {
	return p.volume
}

// SetVolume sets the volume.
// volume must be in between 0 and 1. This function panics otherwise.
func (p *Player) SetVolume(volume float64) {
	// The condition must be true when volume is NaN.
	if !(0 <= volume && volume <= 1) {
		panic("audio: volume must be in between 0 and 1")
	}
	p.volume = volume
}

// TODO: Panning
<|endoftext|>


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

// The gopackages command is a diagnostic tool that demonstrates
// how to use golang.org/x/tools/go/packages to load, parse,
// type-check, and print one or more Go packages.
// Its precise output is unspecified and may change.
package main

import (
	"flag"
	"fmt"
	"go/types"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"runtime/trace"
	"sort"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/go/types/typeutil"
)

// flags
var (
	depsFlag = flag.Bool("deps", false, "show dependencies too")
	testFlag = flag.Bool("test", false, "include any tests implied by the patterns")
	mode     = flag.String("mode", "graph", "mode (one of metadata, graph, typed, alltyped)")
	private  = flag.Bool("private", false, "show non-exported declarations too")

	cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
	memprofile = flag.String("memprofile", "", "write memory profile to this file")
	traceFlag  = flag.String("trace", "", "write trace log to this file")
)

func usage() {
	fmt.Fprintln(os.Stderr, `Usage: gopackages [-deps] [-cgo] [-mode=...] [-private] package...

The gopackages command loads, parses, type-checks,
and prints one or more Go packages.

Packages are specified using the notation of "go list",
or other underlying build system.

Flags:`)
	flag.PrintDefaults()
}

func main() {
	log.SetPrefix("gopackages: ")
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		usage()
		os.Exit(1)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		// NB: profile won't be written in case of error.
		defer pprof.StopCPUProfile()
	}

	if *traceFlag != "" {
		f, err := os.Create(*traceFlag)
		if err != nil {
			log.Fatal(err)
		}
		if err := trace.Start(f); err != nil {
			log.Fatal(err)
		}
		// NB: trace log won't be written in case of error.
		defer func() {
			trace.Stop()
			log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag)
		}()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		// NB: memprofile won't be written in case of error.
		defer func() {
			runtime.GC() // get up-to-date statistics
			if err := pprof.WriteHeapProfile(f); err != nil {
				log.Fatalf("Writing memory profile: %v", err)
			}
			f.Close()
		}()
	}

	// Load, parse, and type-check the packages named on the command line.
	cfg := &packages.Config{
		Mode:  packages.LoadSyntax,
		Error: func(error) {}, // we'll take responsibility for printing errors
		Tests: *testFlag,
	}

	// -mode flag
	switch strings.ToLower(*mode) {
	case "files":
		cfg.Mode = packages.LoadFiles
	case "imports":
		cfg.Mode = packages.LoadImports
	case "types":
		cfg.Mode = packages.LoadTypes
	case "syntax":
		cfg.Mode = packages.LoadSyntax
	case "allsyntax":
		cfg.Mode = packages.LoadAllSyntax
	default:
		log.Fatalf("invalid mode: %s", *mode)
	}

	lpkgs, err := packages.Load(cfg, flag.Args()...)
	if err != nil {
		log.Fatal(err)
	}

	// -deps: print dependencies too.
	if *depsFlag {
		// We can't use packages.All because
		// we need an ordered traversal.
		var all []*packages.Package // postorder
		seen := make(map[*packages.Package]bool)
		var visit func(*packages.Package)
		visit = func(lpkg *packages.Package) {
			if !seen[lpkg] {
				seen[lpkg] = true

				// visit imports
				var importPaths []string
				for path := range lpkg.Imports {
					importPaths = append(importPaths, path)
				}
				sort.Strings(importPaths) // for determinism
				for _, path := range importPaths {
					visit(lpkg.Imports[path])
				}

				all = append(all, lpkg)
			}
		}
		for _, lpkg := range lpkgs {
			visit(lpkg)
		}
		lpkgs = all
	}

	for _, lpkg := range lpkgs {
		print(lpkg)
	}
}

func print(lpkg *packages.Package) {
	// title
	var kind string
	// TODO(matloob): If IsTest is added back print "test command" or
	// "test package" for packages with IsTest == true.
	if lpkg.Name == "main" {
		kind += "command"
	} else {
		kind += "package"
	}
	fmt.Printf("Go %s %q:\n", kind, lpkg.ID) // unique ID
	fmt.Printf("\tpackage %s\n", lpkg.Name)

	// characterize type info
	if lpkg.Types == nil {
		fmt.Printf("\thas no exported type info\n")
	} else if !lpkg.Types.Complete() {
		fmt.Printf("\thas incomplete exported type info\n")
	} else if len(lpkg.Syntax) == 0 {
		fmt.Printf("\thas complete exported type info\n")
	} else {
		fmt.Printf("\thas complete exported type info and typed ASTs\n")
	}
	if lpkg.Types != nil && lpkg.IllTyped && len(lpkg.Errors) == 0 {
		fmt.Printf("\thas an error among its dependencies\n")
	}

	// source files
	for _, src := range lpkg.GoFiles {
		fmt.Printf("\tfile %s\n", src)
	}

	// imports
	var lines []string
	for importPath, imp := range lpkg.Imports {
		var line string
		if imp.ID == importPath {
			line = fmt.Sprintf("\timport %q", importPath)
		} else {
			line = fmt.Sprintf("\timport %q => %q", importPath, imp.ID)
		}
		lines = append(lines, line)
	}
	sort.Strings(lines)
	for _, line := range lines {
		fmt.Println(line)
	}

	// errors
	for _, err := range lpkg.Errors {
		fmt.Printf("\t%s\n", err)
	}

	// package members (TypeCheck or WholeProgram mode)
	if lpkg.Types != nil {
		qual := types.RelativeTo(lpkg.Types)
		scope := lpkg.Types.Scope()
		for _, name := range scope.Names() {
			obj := scope.Lookup(name)
			if !obj.Exported() && !*private {
				continue // skip unexported names
			}

			fmt.Printf("\t%s\n", types.ObjectString(obj, qual))
			if _, ok := obj.(*types.TypeName); ok {
				for _, meth := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
					if !meth.Obj().Exported() && !*private {
						continue // skip unexported names
					}
					fmt.Printf("\t%s\n", types.SelectionString(meth, qual))
				}
			}
		}
	}

	fmt.Println()
}
<commit_msg>go/packages: fix the gopackages command<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The gopackages command is a diagnostic tool that demonstrates
// how to use golang.org/x/tools/go/packages to load, parse,
// type-check, and print one or more Go packages.
// Its precise output is unspecified and may change.
package main

import (
	"flag"
	"fmt"
	"go/types"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"runtime/trace"
	"sort"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/go/types/typeutil"
)

// flags
var (
	depsFlag = flag.Bool("deps", false, "show dependencies too")
	testFlag = flag.Bool("test", false, "include any tests implied by the patterns")
	mode     = flag.String("mode", "imports", "mode (one of files, imports, types, syntax, allsyntax)")
	private  = flag.Bool("private", false, "show non-exported declarations too")

	cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
	memprofile = flag.String("memprofile", "", "write memory profile to this file")
	traceFlag  = flag.String("trace", "", "write trace log to this file")
)

func usage() {
	fmt.Fprintln(os.Stderr, `Usage: gopackages [-deps] [-cgo] [-mode=...] [-private] package...

The gopackages command loads, parses, type-checks,
and prints one or more Go packages.

Packages are specified using the notation of "go list",
or other underlying build system.

Flags:`)
	flag.PrintDefaults()
}

func main() {
	log.SetPrefix("gopackages: ")
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		usage()
		os.Exit(1)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		// NB: profile won't be written in case of error.
		defer pprof.StopCPUProfile()
	}

	if *traceFlag != "" {
		f, err := os.Create(*traceFlag)
		if err != nil {
			log.Fatal(err)
		}
		if err := trace.Start(f); err != nil {
			log.Fatal(err)
		}
		// NB: trace log won't be written in case of error.
		defer func() {
			trace.Stop()
			log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag)
		}()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		// NB: memprofile won't be written in case of error.
		defer func() {
			runtime.GC() // get up-to-date statistics
			if err := pprof.WriteHeapProfile(f); err != nil {
				log.Fatalf("Writing memory profile: %v", err)
			}
			f.Close()
		}()
	}

	// Load, parse, and type-check the packages named on the command line.
	cfg := &packages.Config{
		Mode:  packages.LoadSyntax,
		Error: func(error) {}, // we'll take responsibility for printing errors
		Tests: *testFlag,
	}

	// -mode flag
	switch strings.ToLower(*mode) {
	case "files":
		cfg.Mode = packages.LoadFiles
	case "imports":
		cfg.Mode = packages.LoadImports
	case "types":
		cfg.Mode = packages.LoadTypes
	case "syntax":
		cfg.Mode = packages.LoadSyntax
	case "allsyntax":
		cfg.Mode = packages.LoadAllSyntax
	default:
		log.Fatalf("invalid mode: %s", *mode)
	}

	lpkgs, err := packages.Load(cfg, flag.Args()...)
	if err != nil {
		log.Fatal(err)
	}

	// -deps: print dependencies too.
	if *depsFlag {
		// We can't use packages.All because
		// we need an ordered traversal.
		var all []*packages.Package // postorder
		seen := make(map[*packages.Package]bool)
		var visit func(*packages.Package)
		visit = func(lpkg *packages.Package) {
			if !seen[lpkg] {
				seen[lpkg] = true

				// visit imports
				var importPaths []string
				for path := range lpkg.Imports {
					importPaths = append(importPaths, path)
				}
				sort.Strings(importPaths) // for determinism
				for _, path := range importPaths {
					visit(lpkg.Imports[path])
				}

				all = append(all, lpkg)
			}
		}
		for _, lpkg := range lpkgs {
			visit(lpkg)
		}
		lpkgs = all
	}

	for _, lpkg := range lpkgs {
		print(lpkg)
	}
}

func print(lpkg *packages.Package) {
	// title
	var kind string
	// TODO(matloob): If IsTest is added back print "test command" or
	// "test package" for packages with IsTest == true.
	if lpkg.Name == "main" {
		kind += "command"
	} else {
		kind += "package"
	}
	fmt.Printf("Go %s %q:\n", kind, lpkg.ID) // unique ID
	fmt.Printf("\tpackage %s\n", lpkg.Name)

	// characterize type info
	if lpkg.Types == nil {
		fmt.Printf("\thas no exported type info\n")
	} else if !lpkg.Types.Complete() {
		fmt.Printf("\thas incomplete exported type info\n")
	} else if len(lpkg.Syntax) == 0 {
		fmt.Printf("\thas complete exported type info\n")
	} else {
		fmt.Printf("\thas complete exported type info and typed ASTs\n")
	}
	if lpkg.Types != nil && lpkg.IllTyped && len(lpkg.Errors) == 0 {
		fmt.Printf("\thas an error among its dependencies\n")
	}

	// source files
	for _, src := range lpkg.GoFiles {
		fmt.Printf("\tfile %s\n", src)
	}

	// imports
	var lines []string
	for importPath, imp := range lpkg.Imports {
		var line string
		if imp.ID == importPath {
			line = fmt.Sprintf("\timport %q", importPath)
		} else {
			line = fmt.Sprintf("\timport %q => %q", importPath, imp.ID)
		}
		lines = append(lines, line)
	}
	sort.Strings(lines)
	for _, line := range lines {
		fmt.Println(line)
	}

	// errors
	for _, err := range lpkg.Errors {
		fmt.Printf("\t%s\n", err)
	}

	// package members (TypeCheck or WholeProgram mode)
	if lpkg.Types != nil {
		qual := types.RelativeTo(lpkg.Types)
		scope := lpkg.Types.Scope()
		for _, name := range scope.Names() {
			obj := scope.Lookup(name)
			if !obj.Exported() && !*private {
				continue // skip unexported names
			}

			fmt.Printf("\t%s\n", types.ObjectString(obj, qual))
			if _, ok := obj.(*types.TypeName); ok {
				for _, meth := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
					if !meth.Obj().Exported() && !*private {
						continue // skip unexported names
					}
					fmt.Printf("\t%s\n", types.SelectionString(meth, qual))
				}
			}
		}
	}

	fmt.Println()
}
<|endoftext|>