<commit_before>package main

import "github.com/jpki/myna/libmyna"

import (
	"crypto/x509"
	_ "errors"
	"fmt"
	"github.com/mattn/go-gtk/gdkpixbuf"
	"github.com/mattn/go-gtk/glib"
	"github.com/mattn/go-gtk/gtk"
	"github.com/urfave/cli"
	"os"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "mynag"
	app.Usage = "マイナクライアント(GUI)"
	app.Author = "HAMANO Tsukasa"
	app.Email = "hamano@osstech.co.jp"
	app.Version = libmyna.Version
	app.Action = mynag
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "詳細出力",
		},
	}
	app.Run(os.Args)
}

func mynag(c *cli.Context) error {
	gtk.Init(&os.Args)
	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetPosition(gtk.WIN_POS_CENTER)
	window.SetTitle(c.App.Usage)
	window.SetIconName("application-certificate")
	window.Connect("destroy", func(ctx *glib.CallbackContext) {
		gtk.MainQuit()
	})

	// create menu
	menu := gtk.NewMenuBar()
	menuHelp := gtk.NewMenuItemWithMnemonic("Menu")
	menu.Append(menuHelp)

	menuSub := gtk.NewMenu()
	menuHelp.SetSubmenu(menuSub)
	menuItemAbout := gtk.NewMenuItemWithMnemonic("About")
	menuItemAbout.Connect("activate", onAbout, c)
	menuSub.Append(menuItemAbout)

	menuItemQuit := gtk.NewMenuItemWithMnemonic("Quit")
	menuItemQuit.Connect("activate", onQuit, c)
	menuSub.Append(menuItemQuit)

	// create button box
	boxButton := gtk.NewVBox(false, 1)
	buttonTest := gtk.NewButtonWithLabel("動作確認")
	buttonTest.Clicked(onTest, c)
	boxButton.Add(buttonTest)

	buttonCardInfo := gtk.NewButtonWithLabel("券面事項確認")
	buttonCardInfo.Clicked(onCardInfo, c)
	boxButton.Add(buttonCardInfo)

	buttonShowCert := gtk.NewButtonWithLabel("証明書表示")
	buttonShowCert.Clicked(onShowCert, c)
	boxButton.Add(buttonShowCert)

	buttonSign := gtk.NewButtonWithLabel("署名")
	buttonSign.Clicked(onSign, c)
	boxButton.Add(buttonSign)

	buttonPinStatus := gtk.NewButtonWithLabel("PINステータス")
	buttonPinStatus.Clicked(onPinStatus, c)
	boxButton.Add(buttonPinStatus)

	// create hbox
	hbox := gtk.NewHBox(false, 1)
	//dir, _ := filepath.Split(os.Args[0])
	//imagefile := filepath.Join(dir, "usagi.png")
	//imageLogo := gtk.NewImageFromFile(imagefile)
	imageData, _ := Asset("usagi.png")
	imageBuf, _ := gdkpixbuf.NewPixbufFromData(imageData)
	imageLogo := gtk.NewImageFromPixbuf(imageBuf)
	hbox.Add(imageLogo)
	hbox.Add(boxButton)

	// create main box
	boxMain := gtk.NewVBox(false, 1)
	boxMain.PackStart(menu, false, false, 0)
	boxMain.Add(hbox)

	window.Add(boxMain)
	//window.SetSizeRequest(400, 400)
	window.ShowAll()
	gtk.Main()
	return nil
}

func onAbout(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	dialog := gtk.NewAboutDialog()
	dialog.SetName("About")
	dialog.SetProgramName(c.App.Name)
	dialog.SetAuthors([]string{c.App.Author})
	//dialog.SetLicense("ライセンス")
	dialog.SetWrapLicense(true)
	dialog.Run()
	dialog.Destroy()
}

func onQuit(ctx *glib.CallbackContext) {
	_ = ctx.Data().(*cli.Context)
	gtk.MainQuit()
}

func showErrorMsg(msg string) {
	dialog := gtk.NewMessageDialog(
		nil,
		gtk.DIALOG_MODAL,
		gtk.MESSAGE_INFO,
		gtk.BUTTONS_OK,
		msg)
	dialog.Response(func() {
		dialog.Destroy()
	})
	dialog.Run()
}

func showMsg(msg string) {
	dialog := gtk.NewMessageDialog(
		nil,
		gtk.DIALOG_MODAL,
		gtk.MESSAGE_INFO,
		gtk.BUTTONS_OK,
		msg)
	dialog.Response(func() {
		dialog.Destroy()
	})
	dialog.Run()
}

func onTest(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	err := libmyna.CheckCard(c)
	var msg string
	if err != nil {
		msg = err.Error()
	} else {
		msg = "正常です。"
	}
	showErrorMsg(msg)
}

func popupPrompt(title string) string {
	dialog := gtk.NewDialog()
	dialog.SetTitle(title)
	vbox := dialog.GetVBox()
	label := gtk.NewLabel(title)
	vbox.Add(label)
	pinEntry := gtk.NewEntry()
	pinEntry.SetVisibility(false)
	pinEntry.Connect("activate", func() {
		dialog.Hide()
		dialog.Response(gtk.RESPONSE_OK)
	})
	vbox.Add(pinEntry)
	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.SetDefaultResponse(gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	pin := pinEntry.GetText()
	dialog.Destroy()
	if res == gtk.RESPONSE_OK {
		return pin
	} else {
		return ""
	}
}

func popupPinPrompt() string {
	return popupPrompt("暗証番号(4桁)")
}

func popupPasswordPrompt() string {
	pass := popupPrompt("署名用パスワード(6-16桁)")
	if len(pass) < 6 || 16 < len(pass) {
		showErrorMsg("署名用パスワード(6-16桁)を入力してください。")
		return ""
	}
	return strings.ToUpper(pass)
}

func onCardInfo(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	err := libmyna.CheckCard(c)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	pin := popupPinPrompt()
	if pin == "" {
		return
	}
	info, _ := libmyna.GetCardInfo(c, pin)
	var msg string
	msg += fmt.Sprintf("個人番号: %s\n", info["number"])
	msg += fmt.Sprintf("氏名:     %s\n", info["name"])
	msg += fmt.Sprintf("住所:     %s\n", info["address"])
	msg += fmt.Sprintf("生年月日: %s\n", info["birth"])
	msg += fmt.Sprintf("性別:     %s", info["sex"])
	showMsg(msg)
}

func onPinStatus(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	status, _ := libmyna.GetPinStatus(c)
	var msg string
	msg += fmt.Sprintf("認証用PIN: のこり%d回\n", status["auth"])
	msg += fmt.Sprintf("署名用PIN: のこり%d回\n", status["sign"])
	msg += fmt.Sprintf("券面入力補助PIN: のこり%d回\n", status["card"])
	msg += fmt.Sprintf("謎のPIN1: のこり%d回\n", status["unknown1"])
	msg += fmt.Sprintf("謎のPIN2: のこり%d回", status["unknown2"])
	showMsg(msg)
}

func onShowCert(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	rc := selectCert()
	var title string
	var ef string
	var pin string
	switch rc {
	case 0:
		// キャンセル
		return
	case 1:
		title = "認証用証明書"
		ef = "00 0A"
	case 2:
		title = "署名用証明書"
		ef = "00 01"
		pin = popupPasswordPrompt()
		if pin == "" {
			return
		}
	case 3:
		title = "認証用CA証明書"
		ef = "00 0B"
	case 4:
		title = "署名用CA証明書"
		ef = "00 02"
	}
	cert, err := libmyna.GetCert(c, ef, pin)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	popupCertDialog(title, cert)
}

func popupCertDialog(title string, cert *x509.Certificate) {
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle(title)
	vbox := dialog.GetVBox()

	vbox.PackStart(gtk.NewLabel("Subject: "), false, false, 10)
	subjectEntry := gtk.NewEntry()
	subjectEntry.SetText(libmyna.Name2String(cert.Subject))
	subjectEntry.SetWidthChars(64)
	vbox.PackStart(subjectEntry, false, false, 10)
	vbox.PackStart(gtk.NewLabel("Issuer: "), false, false, 10)
	issuerEntry := gtk.NewEntry()
	issuerEntry.SetText(libmyna.Name2String(cert.Issuer))
	issuerEntry.SetWidthChars(64)
	vbox.PackStart(issuerEntry, false, false, 10)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.ShowAll()
	dialog.Run()
}

func selectCert() int {
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle("証明書選択")
	vbox := dialog.GetVBox()
	radio1 := gtk.NewRadioButtonWithLabel(nil, "認証用証明書")
	radio2 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "署名用証明書")
	radio3 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "認証用CA証明書")
	radio4 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "署名用CA証明書")

	vbox.PackStart(radio1, false, false, 10)
	vbox.PackStart(radio2, false, false, 10)
	vbox.PackStart(radio3, false, false, 10)
	vbox.PackStart(radio4, false, false, 10)

	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.SetDefaultResponse(gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	if res == gtk.RESPONSE_OK {
		if radio1.GetActive() {
			return 1
		} else if radio2.GetActive() {
			return 2
		} else if radio3.GetActive() {
			return 3
		} else if radio4.GetActive() {
			return 4
		} else {
			return 0
		}
	} else {
		return 0
	}
}

func onSign(ctx *glib.CallbackContext) {
	//c := ctx.Data().(*cli.Context)
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle("署名")

	inputEntry := gtk.NewEntry()
	inputEntry.SetWidthChars(64)
	inputEntry.SetEditable(false)
	outputEntry := gtk.NewEntry()
	outputEntry.SetWidthChars(64)
	outputEntry.SetEditable(false)

	vbox := dialog.GetVBox()
	inputButton := gtk.NewButtonWithLabel("入力ファイル")
	inputButton.Clicked(func() {
		filedialog := gtk.NewFileChooserDialog(
			"ファイル選択",
			nil,
			gtk.FILE_CHOOSER_ACTION_OPEN,
			gtk.STOCK_OK,
			gtk.RESPONSE_ACCEPT)
		filedialog.Response(func() {
			filename := filedialog.GetFilename()
			inputEntry.SetText(filename)
			filedialog.Destroy()
			if outputEntry.GetText() == "" {
				outputEntry.SetText(filename + ".p7s")
			}
		})
		filedialog.Run()
	})
	row := gtk.NewHBox(false, 1)
	row.Add(inputEntry)
	row.Add(inputButton)
	vbox.Add(row)

	outputButton := gtk.NewButtonWithLabel("出力ファイル")
	outputButton.Clicked(func() {
		filedialog := gtk.NewFileChooserDialog(
			"ファイル選択",
			nil,
			gtk.FILE_CHOOSER_ACTION_SAVE,
			gtk.STOCK_OK,
			gtk.RESPONSE_ACCEPT)
		filedialog.Response(func() {
			outputEntry.SetText(filedialog.GetFilename())
			filedialog.Destroy()
		})
		filedialog.Run()

	})
	row = gtk.NewHBox(false, 1)
	row.Add(outputEntry)
	row.Add(outputButton)
	vbox.Add(row)

	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton("署名", gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	if res != gtk.RESPONSE_OK {
		return
	}
	inputFile := inputEntry.GetText()
	if inputFile == "" {
		showErrorMsg("入力ファイルを指定してください")
		return
	}
	outputFile := outputEntry.GetText()
	if outputFile == "" {
		showErrorMsg("出力ファイルを指定してください")
		return
	}
	fmt.Printf("input: %s\n", inputFile)
	fmt.Printf("output: %s\n", outputFile)
}
<commit_msg>署名UIの実装<commit_after>package main

import "github.com/jpki/myna/libmyna"

import (
	"crypto/x509"
	_ "errors"
	"fmt"
	"github.com/mattn/go-gtk/gdkpixbuf"
	"github.com/mattn/go-gtk/glib"
	"github.com/mattn/go-gtk/gtk"
	"github.com/urfave/cli"
	"os"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "mynag"
	app.Description = "マイナクライアント(GUI)"
	app.Author = "HAMANO Tsukasa"
	app.Email = "hamano@osstech.co.jp"
	app.Version = libmyna.Version
	app.Action = mynag
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "詳細出力",
		},
	}
	app.Run(os.Args)
}

func mynag(c *cli.Context) error {
	gtk.Init(&os.Args)
	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetPosition(gtk.WIN_POS_CENTER)
	window.SetTitle(c.App.Description)
	window.SetIconName("application-certificate")
	window.Connect("destroy", func(ctx *glib.CallbackContext) {
		gtk.MainQuit()
	})

	// create menu
	menu := gtk.NewMenuBar()
	menuHelp := gtk.NewMenuItemWithMnemonic("Menu")
	menu.Append(menuHelp)

	menuSub := gtk.NewMenu()
	menuHelp.SetSubmenu(menuSub)
	menuItemAbout := gtk.NewMenuItemWithMnemonic("About")
	menuItemAbout.Connect("activate", onAbout, c)
	menuSub.Append(menuItemAbout)

	menuItemQuit := gtk.NewMenuItemWithMnemonic("Quit")
	menuItemQuit.Connect("activate", onQuit, c)
	menuSub.Append(menuItemQuit)

	// create button box
	boxButton := gtk.NewVBox(false, 1)
	buttonTest := gtk.NewButtonWithLabel("動作確認")
	buttonTest.Clicked(onTest, c)
	boxButton.Add(buttonTest)

	buttonCardInfo := gtk.NewButtonWithLabel("券面事項確認")
	buttonCardInfo.Clicked(onCardInfo, c)
	boxButton.Add(buttonCardInfo)

	buttonShowCert := gtk.NewButtonWithLabel("証明書表示")
	buttonShowCert.Clicked(onShowCert, c)
	boxButton.Add(buttonShowCert)

	buttonSign := gtk.NewButtonWithLabel("署名")
	buttonSign.Clicked(onSign, c)
	boxButton.Add(buttonSign)

	buttonPinStatus := gtk.NewButtonWithLabel("PINステータス")
	buttonPinStatus.Clicked(onPinStatus, c)
	boxButton.Add(buttonPinStatus)

	// create hbox
	hbox := gtk.NewHBox(false, 1)
	//dir, _ := filepath.Split(os.Args[0])
	//imagefile := filepath.Join(dir, "usagi.png")
	//imageLogo := gtk.NewImageFromFile(imagefile)
	imageData, _ := Asset("usagi.png")
	imageBuf, _ := gdkpixbuf.NewPixbufFromData(imageData)
	imageLogo := gtk.NewImageFromPixbuf(imageBuf)
	hbox.Add(imageLogo)
	hbox.Add(boxButton)

	// create main box
	boxMain := gtk.NewVBox(false, 1)
	boxMain.PackStart(menu, false, false, 0)
	boxMain.Add(hbox)

	window.Add(boxMain)
	//window.SetSizeRequest(400, 400)
	window.ShowAll()
	gtk.Main()
	return nil
}

func onAbout(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	dialog := gtk.NewAboutDialog()
	dialog.SetName("About")
	dialog.SetProgramName(c.App.Description)
	dialog.SetAuthors([]string{c.App.Author})
	comments := `
このソフトウェアはMITライセンスで配布されます。
GTK+ は LGPLライセンス
mattn ware の go-gtk
かわいいウサギのイラストはいらすとやの著作物です
`
	dialog.SetComments(comments)
	dialog.SetWebsite("https://github.com/jpki/myna")
	license, _ := Asset("../LICENSE")
	dialog.SetLicense(string(license))
	dialog.SetWrapLicense(true)
	imageData, _ := Asset("usagi.png")
	imageBuf, _ := gdkpixbuf.NewPixbufFromData(imageData)
	dialog.SetLogo(imageBuf)
	dialog.Run()
	dialog.Destroy()
}

func onQuit(ctx *glib.CallbackContext) {
	gtk.MainQuit()
}

func showErrorMsg(msg string) {
	dialog := gtk.NewMessageDialog(
		nil,
		gtk.DIALOG_MODAL,
		gtk.MESSAGE_INFO,
		gtk.BUTTONS_OK,
		msg)
	dialog.Response(func() {
		dialog.Destroy()
	})
	dialog.Run()
}

func showMsg(msg string) {
	dialog := gtk.NewMessageDialog(
		nil,
		gtk.DIALOG_MODAL,
		gtk.MESSAGE_INFO,
		gtk.BUTTONS_OK,
		msg)
	dialog.Response(func() {
		dialog.Destroy()
	})
	dialog.Run()
}

func onTest(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	err := libmyna.CheckCard(c)
	var msg string
	if err != nil {
		msg = err.Error()
	} else {
		msg = "正常です。"
	}
	showErrorMsg(msg)
}

func popupPrompt(title string) string {
	dialog := gtk.NewDialog()
	dialog.SetTitle(title)
	vbox := dialog.GetVBox()
	label := gtk.NewLabel(title)
	vbox.Add(label)
	pinEntry := gtk.NewEntry()
	pinEntry.SetVisibility(false)
	pinEntry.Connect("activate", func() {
		dialog.Hide()
		dialog.Response(gtk.RESPONSE_OK)
	})
	vbox.Add(pinEntry)
	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.SetDefaultResponse(gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	pin := pinEntry.GetText()
	dialog.Destroy()
	if res == gtk.RESPONSE_OK {
		return pin
	} else {
		return ""
	}
}

func popupPinPrompt() string {
	return popupPrompt("暗証番号(4桁)")
}

func popupPasswordPrompt() string {
	pass := popupPrompt("署名用パスワード(6-16桁)")
	if len(pass) < 6 || 16 < len(pass) {
		showErrorMsg("署名用パスワード(6-16桁)を入力してください。")
		return ""
	}
	return strings.ToUpper(pass)
}

func onCardInfo(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	err := libmyna.CheckCard(c)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	pin := popupPinPrompt()
	if pin == "" {
		return
	}
	info, _ := libmyna.GetCardInfo(c, pin)
	var msg string
	msg += fmt.Sprintf("個人番号: %s\n", info["number"])
	msg += fmt.Sprintf("氏名:     %s\n", info["name"])
	msg += fmt.Sprintf("住所:     %s\n", info["address"])
	msg += fmt.Sprintf("生年月日: %s\n", info["birth"])
	msg += fmt.Sprintf("性別:     %s", info["sex"])
	showMsg(msg)
}

func onPinStatus(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	status, err := libmyna.GetPinStatus(c)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	var msg string
	msg += fmt.Sprintf("認証用PIN: のこり%d回\n", status["auth"])
	msg += fmt.Sprintf("署名用PIN: のこり%d回\n", status["sign"])
	msg += fmt.Sprintf("券面入力補助PIN: のこり%d回\n", status["card"])
	msg += fmt.Sprintf("謎のPIN1: のこり%d回\n", status["unknown1"])
	msg += fmt.Sprintf("謎のPIN2: のこり%d回", status["unknown2"])
	showMsg(msg)
}

func onShowCert(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	rc := selectCert()
	var title string
	var ef string
	var pin string
	switch rc {
	case 0:
		// キャンセル
		return
	case 1:
		title = "認証用証明書"
		ef = "00 0A"
	case 2:
		title = "署名用証明書"
		ef = "00 01"
		pin = popupPasswordPrompt()
		if pin == "" {
			return
		}
	case 3:
		title = "認証用CA証明書"
		ef = "00 0B"
	case 4:
		title = "署名用CA証明書"
		ef = "00 02"
	}
	cert, err := libmyna.GetCert(c, ef, pin)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	popupCertDialog(title, cert)
}

func popupCertDialog(title string, cert *x509.Certificate) {
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle(title)
	vbox := dialog.GetVBox()

	vbox.PackStart(gtk.NewLabel("Subject: "), false, false, 10)
	subjectEntry := gtk.NewEntry()
	subjectEntry.SetText(libmyna.Name2String(cert.Subject))
	subjectEntry.SetWidthChars(64)
	vbox.PackStart(subjectEntry, false, false, 10)
	vbox.PackStart(gtk.NewLabel("Issuer: "), false, false, 10)
	issuerEntry := gtk.NewEntry()
	issuerEntry.SetText(libmyna.Name2String(cert.Issuer))
	issuerEntry.SetWidthChars(64)
	vbox.PackStart(issuerEntry, false, false, 10)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.ShowAll()
	dialog.Run()
}

func selectCert() int {
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle("証明書選択")
	vbox := dialog.GetVBox()
	radio1 := gtk.NewRadioButtonWithLabel(nil, "認証用証明書")
	radio2 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "署名用証明書")
	radio3 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "認証用CA証明書")
	radio4 := gtk.NewRadioButtonWithLabel(radio1.GetGroup(), "署名用CA証明書")

	vbox.PackStart(radio1, false, false, 10)
	vbox.PackStart(radio2, false, false, 10)
	vbox.PackStart(radio3, false, false, 10)
	vbox.PackStart(radio4, false, false, 10)

	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
	dialog.SetDefaultResponse(gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	if res == gtk.RESPONSE_OK {
		if radio1.GetActive() {
			return 1
		} else if radio2.GetActive() {
			return 2
		} else if radio3.GetActive() {
			return 3
		} else if radio4.GetActive() {
			return 4
		} else {
			return 0
		}
	} else {
		return 0
	}
}

func onSign(ctx *glib.CallbackContext) {
	c := ctx.Data().(*cli.Context)
	dialog := gtk.NewDialog()
	defer dialog.Destroy()
	dialog.SetTitle("署名")

	inputEntry := gtk.NewEntry()
	inputEntry.SetWidthChars(64)
	inputEntry.SetEditable(false)
	outputEntry := gtk.NewEntry()
	outputEntry.SetWidthChars(64)
	outputEntry.SetEditable(false)

	vbox := dialog.GetVBox()
	inputButton := gtk.NewButtonWithLabel("入力ファイル")
	inputButton.Clicked(func() {
		filedialog := gtk.NewFileChooserDialog(
			"ファイル選択",
			nil,
			gtk.FILE_CHOOSER_ACTION_OPEN,
			gtk.STOCK_OK,
			gtk.RESPONSE_OK,
			gtk.STOCK_CANCEL,
			gtk.RESPONSE_CANCEL)
		res := filedialog.Run()
		if res == gtk.RESPONSE_OK {
			filename := filedialog.GetFilename()
			inputEntry.SetText(filename)
			if outputEntry.GetText() == "" {
				outputEntry.SetText(filename + ".p7s")
			}
		}
		filedialog.Destroy()
	})
	row := gtk.NewHBox(false, 1)
	row.Add(inputEntry)
	row.Add(inputButton)
	vbox.Add(row)

	outputButton := gtk.NewButtonWithLabel("出力ファイル")
	outputButton.Clicked(func() {
		filedialog := gtk.NewFileChooserDialog(
			"ファイル選択",
			nil,
			gtk.FILE_CHOOSER_ACTION_SAVE,
			gtk.STOCK_OK,
			gtk.RESPONSE_OK,
			gtk.STOCK_CANCEL,
			gtk.RESPONSE_CANCEL)
		res := filedialog.Run()
		if res == gtk.RESPONSE_OK {
			outputEntry.SetText(filedialog.GetFilename())
		}
		filedialog.Destroy()
	})
	row = gtk.NewHBox(false, 1)
	row.Add(outputEntry)
	row.Add(outputButton)
	vbox.Add(row)

	dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
	dialog.AddButton("署名", gtk.RESPONSE_OK)
	dialog.ShowAll()
	res := dialog.Run()
	if res != gtk.RESPONSE_OK {
		return
	}
	inFile := inputEntry.GetText()
	if inFile == "" {
		showErrorMsg("入力ファイルを指定してください")
		return
	}
	outFile := outputEntry.GetText()
	if outFile == "" {
		showErrorMsg("出力ファイルを指定してください")
		return
	}
	pass := popupPasswordPrompt()
	if pass == "" {
		return
	}

	err := libmyna.Sign(c, pass, inFile, outFile)
	if err != nil {
		showErrorMsg(err.Error())
		return
	}
	showMsg("署名完了")
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package prompt

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strings"
	"unicode"

	"github.com/decred/dcrutil/hdkeychain"
	"github.com/decred/dcrwallet/walletseed"
	"golang.org/x/crypto/ssh/terminal"
)

// ProvideSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func ProvideSeed() ([]byte, error) {
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("Enter existing wallet seed: ")
		seedStr, err := reader.ReadString('\n')
		if err != nil {
			return nil, err
		}
		seedStr = strings.TrimSpace(strings.ToLower(seedStr))

		seed, err := hex.DecodeString(seedStr)
		if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
			len(seed) > hdkeychain.MaxSeedBytes {

			fmt.Printf("Invalid seed specified.  Must be a "+
				"hexadecimal value that is at least %d bits and "+
				"at most %d bits\n", hdkeychain.MinSeedBytes*8,
				hdkeychain.MaxSeedBytes*8)
			continue
		}

		return seed, nil
	}
}

// ProvidePrivPassphrase is used to prompt for the private passphrase which
// maybe required during upgrades.
func ProvidePrivPassphrase() ([]byte, error) {
	prompt := "Enter the private passphrase of your wallet: "
	for {
		fmt.Print(prompt)
		pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		pass = bytes.TrimSpace(pass)
		if len(pass) == 0 {
			continue
		}

		return pass, nil
	}
}

// promptList prompts the user with the given prefix, list of valid responses,
// and default list entry to use.  The function will repeat the prompt to the
// user until they enter a valid response.
func promptList(reader *bufio.Reader, prefix string, validResponses []string, defaultEntry string) (string, error) {
	// Setup the prompt according to the parameters.
	validStrings := strings.Join(validResponses, "/")
	var prompt string
	if defaultEntry != "" {
		prompt = fmt.Sprintf("%s (%s) [%s]: ", prefix, validStrings,
			defaultEntry)
	} else {
		prompt = fmt.Sprintf("%s (%s): ", prefix, validStrings)
	}

	// Prompt the user until one of the valid responses is given.
	for {
		fmt.Print(prompt)
		reply, err := reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		reply = strings.TrimSpace(strings.ToLower(reply))
		if reply == "" {
			reply = defaultEntry
		}

		for _, validResponse := range validResponses {
			if reply == validResponse {
				return reply, nil
			}
		}
	}
}

// promptListBool prompts the user for a boolean (yes/no) with the given prefix.
// The function will repeat the prompt to the user until they enter a valid
// reponse.
func promptListBool(reader *bufio.Reader, prefix string, defaultEntry string) (bool, error) {
	// Setup the valid responses.
	valid := []string{"n", "no", "y", "yes"}
	response, err := promptList(reader, prefix, valid, defaultEntry)
	if err != nil {
		return false, err
	}
	return response == "yes" || response == "y", nil
}

// PassPrompt prompts the user for a passphrase with the given prefix.  The
// function will ask the user to confirm the passphrase and will repeat the
// prompts until they enter a matching response.
func PassPrompt(reader *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
	// Prompt the user until they enter a passphrase.
	prompt := fmt.Sprintf("%s: ", prefix)
	for {
		fmt.Print(prompt)
		var pass []byte
		var err error
		fd := int(os.Stdin.Fd())
		if terminal.IsTerminal(fd) {
			pass, err = terminal.ReadPassword(fd)
		} else {
			pass, err = reader.ReadBytes('\n')
			if err == io.EOF {
				err = nil
			}
		}
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		pass = bytes.TrimSpace(pass)
		if len(pass) == 0 {
			continue
		}

		if !confirm {
			return pass, nil
		}

		fmt.Print("Confirm passphrase: ")
		confirm, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		confirm = bytes.TrimSpace(confirm)
		if !bytes.Equal(pass, confirm) {
			fmt.Println("The entered passphrases do not match")
			continue
		}

		return pass, nil
	}
}

// PrivatePass prompts the user for a private passphrase.  All prompts are
// repeated until the user enters a valid response.
func PrivatePass(reader *bufio.Reader) ([]byte, error) {
	return PassPrompt(reader, "Enter the private passphrase for your new wallet", true)
}

// PublicPass prompts the user whether they want to add an additional layer of
// encryption to the wallet.  When the user answers yes and there is already a
// public passphrase provided via the passed config, it prompts them whether or
// not to use that configured passphrase.  It will also detect when the same
// passphrase is used for the private and public passphrase and prompt the user
// if they are sure they want to use the same passphrase for both.  Finally, all
// prompts are repeated until the user enters a valid response.
func PublicPass(reader *bufio.Reader, privPass []byte,
	defaultPubPassphrase, configPubPass []byte) ([]byte, error) {

	pubPass := defaultPubPassphrase
	usePubPass, err := promptListBool(reader, "Do you want "+
		"to add an additional layer of encryption for public "+
		"data?", "no")
	if err != nil {
		return nil, err
	}

	if !usePubPass {
		return pubPass, nil
	}

	if len(configPubPass) != 0 && !bytes.Equal(configPubPass, pubPass) {
		useExisting, err := promptListBool(reader, "Use the "+
			"existing configured public passphrase for encryption "+
			"of public data?", "no")
		if err != nil {
			return nil, err
		}

		if useExisting {
			return configPubPass, nil
		}
	}

	for {
		pubPass, err = PassPrompt(reader, "Enter the public "+
			"passphrase for your new wallet", true)
		if err != nil {
			return nil, err
		}

		if bytes.Equal(pubPass, privPass) {
			useSamePass, err := promptListBool(reader,
				"Are you sure want to use the same passphrase "+
					"for public and private data?", "no")
			if err != nil {
				return nil, err
			}

			if useSamePass {
				break
			}

			continue
		}

		break
	}

	fmt.Println("NOTE: Use the --walletpass option to configure your " +
		"public passphrase.")
	return pubPass, nil
}

// Seed prompts the user whether they want to use an existing wallet generation
// seed.  When the user answers no, a seed will be generated and displayed to
// the user along with prompting them for confirmation.  When the user answers
// yes, a the user is prompted for it.  All prompts are repeated until the user
// enters a valid response. The bool returned indicates if the wallet was
// restored from a given seed or not.
func Seed(reader *bufio.Reader) ([]byte, error) {
	// Ascertain the wallet generation seed.
	useUserSeed, err := promptListBool(reader, "Do you have an "+
		"existing wallet seed you want to use?", "no")
	if err != nil {
		return nil, err
	}
	if !useUserSeed {
		seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
		if err != nil {
			return nil, err
		}

		seedStrSplit := walletseed.EncodeMnemonicSlice(seed)

		fmt.Println("Your wallet generation seed is:")
		for i := 0; i < hdkeychain.RecommendedSeedLen+1; i++ {
			fmt.Printf("%v ", seedStrSplit[i])

			if (i+1)%6 == 0 {
				fmt.Printf("\n")
			}
		}

		fmt.Printf("\n\nHex: %x\n", seed)
		fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" +
			"will NOT be able to restore your wallet without it.")
		fmt.Println("Please keep in mind that anyone who has access\n" +
			"to the seed can also restore your wallet thereby\n" +
			"giving them access to all your funds, so it is\n" +
			"imperative that you keep it in a secure location.")

		for {
			fmt.Print(`Once you have stored the seed in a safe ` +
				`and secure location, enter "OK" to continue: `)
			confirmSeed, err := reader.ReadString('\n')
			if err != nil {
				return nil, err
			}
			confirmSeed = strings.TrimSpace(confirmSeed)
			confirmSeed = strings.Trim(confirmSeed, `"`)
			if confirmSeed == "OK" {
				break
			}
		}

		return seed, nil
	}

	for {
		fmt.Print("Enter existing wallet seed " +
			"(followed by a blank line): ")

		// Use scanner instead of buffio.Reader so we can choose choose
		// more complicated ending condition rather than just a single
		// newline.
		var seedStr string
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			line := scanner.Text()
			if line == "" {
				break
			}
			seedStr += " " + line
		}
		seedStrTrimmed := strings.TrimSpace(seedStr)
		seedStrTrimmed = collapseSpace(seedStrTrimmed)
		wordCount := strings.Count(seedStrTrimmed, " ") + 1

		var seed []byte
		if wordCount == 1 {
			if len(seedStrTrimmed)%2 != 0 {
				seedStrTrimmed = "0" + seedStrTrimmed
			}
			seed, err = hex.DecodeString(seedStrTrimmed)
			if err != nil {
				fmt.Printf("Input error: %v\n", err.Error())
			}
		} else {
			seed, err = walletseed.DecodeUserInput(seedStrTrimmed)
			if err != nil {
				fmt.Printf("Input error: %v\n", err.Error())
			}
		}
		if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
			len(seed) > hdkeychain.MaxSeedBytes {
			fmt.Printf("Invalid seed specified.  Must be a "+
				"word seed (usually 33 words) using the PGP wordlist or "+
				"hexadecimal value that is at least %d bits and "+
				"at most %d bits\n", hdkeychain.MinSeedBytes*8,
				hdkeychain.MaxSeedBytes*8)
			continue
		}

		fmt.Printf("\nSeed input successful. \nHex: %x\n", seed)

		return seed, nil
	}
}

// Setup prompts for, from a buffered reader, the private and/or public
// encryption passphrases to secure a wallet and a previously derived wallet
// seed to use, if any.  privPass and pubPass will always be non-nil values
// (private encryption is required and choosing to not use public data
// encryption will still encrypt the data with an insecure default), and a
// randomly generated seed of the recommended length will be generated and
// returned after the user has confirmed the seed has been backed up to a secure
// location.
//
// The configPubPass parameter is optional (nil should be used to represent the
// lack of a value).  When non-nil, this value represents a public passphrase
// previously specified in a configuration file.  The user will be given the
// option of using this passphrase if public data encryption is enabled,
// otherwise a user-specified passphrase will be prompted for.
func Setup(r *bufio.Reader, insecurePubPass, configPubPass []byte) (privPass, pubPass, seed []byte, err error) {
	// Decred: no legacy keystore restore is needed (first decred wallet
	// version did not use the legacy keystore from earlier versions of
	// btcwallet).

	// Start by prompting for the private passphrase.  When there is an
	// existing keystore, the user will be promped for that passphrase,
	// otherwise they will be prompted for a new one.
	privPass, err = PrivatePass(r)
	if err != nil {
		return
	}

	// Ascertain the public passphrase.  This will either be a value
	// specified by the user or the default hard-coded public passphrase if
	// the user does not want the additional public data encryption.
	pubPass, err = PublicPass(r, privPass, insecurePubPass, configPubPass)
	if err != nil {
		return
	}

	// Ascertain the wallet generation seed.  This will either be an
	// automatically generated value the user has already confirmed or a
	// value the user has entered which has already been validated.
	seed, err = Seed(r)

	return
}

// collapseSpace takes a string and replaces any repeated areas of whitespace
// with a single space character.
func collapseSpace(in string) string {
	whiteSpace := false
	out := ""
	for _, c := range in {
		if unicode.IsSpace(c) {
			if !whiteSpace {
				out = out + " "
			}
			whiteSpace = true
		} else {
			out = out + string(c)
			whiteSpace = false
		}
	}
	return out
}
<commit_msg>Fixes #806  Prompt accepts "ok" (#807)<commit_after>// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package prompt

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strings"
	"unicode"

	"github.com/decred/dcrutil/hdkeychain"
	"github.com/decred/dcrwallet/walletseed"
	"golang.org/x/crypto/ssh/terminal"
)

// ProvideSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func ProvideSeed() ([]byte, error) {
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("Enter existing wallet seed: ")
		seedStr, err := reader.ReadString('\n')
		if err != nil {
			return nil, err
		}
		seedStr = strings.TrimSpace(strings.ToLower(seedStr))

		seed, err := hex.DecodeString(seedStr)
		if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
			len(seed) > hdkeychain.MaxSeedBytes {

			fmt.Printf("Invalid seed specified.  Must be a "+
				"hexadecimal value that is at least %d bits and "+
				"at most %d bits\n", hdkeychain.MinSeedBytes*8,
				hdkeychain.MaxSeedBytes*8)
			continue
		}

		return seed, nil
	}
}

// ProvidePrivPassphrase is used to prompt for the private passphrase which
// maybe required during upgrades.
func ProvidePrivPassphrase() ([]byte, error) {
	prompt := "Enter the private passphrase of your wallet: "
	for {
		fmt.Print(prompt)
		pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		pass = bytes.TrimSpace(pass)
		if len(pass) == 0 {
			continue
		}

		return pass, nil
	}
}

// promptList prompts the user with the given prefix, list of valid responses,
// and default list entry to use.  The function will repeat the prompt to the
// user until they enter a valid response.
func promptList(reader *bufio.Reader, prefix string, validResponses []string, defaultEntry string) (string, error) {
	// Setup the prompt according to the parameters.
	validStrings := strings.Join(validResponses, "/")
	var prompt string
	if defaultEntry != "" {
		prompt = fmt.Sprintf("%s (%s) [%s]: ", prefix, validStrings,
			defaultEntry)
	} else {
		prompt = fmt.Sprintf("%s (%s): ", prefix, validStrings)
	}

	// Prompt the user until one of the valid responses is given.
	for {
		fmt.Print(prompt)
		reply, err := reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		reply = strings.TrimSpace(strings.ToLower(reply))
		if reply == "" {
			reply = defaultEntry
		}

		for _, validResponse := range validResponses {
			if reply == validResponse {
				return reply, nil
			}
		}
	}
}

// promptListBool prompts the user for a boolean (yes/no) with the given prefix.
// The function will repeat the prompt to the user until they enter a valid
// reponse.
func promptListBool(reader *bufio.Reader, prefix string, defaultEntry string) (bool, error) {
	// Setup the valid responses.
	valid := []string{"n", "no", "y", "yes"}
	response, err := promptList(reader, prefix, valid, defaultEntry)
	if err != nil {
		return false, err
	}
	return response == "yes" || response == "y", nil
}

// PassPrompt prompts the user for a passphrase with the given prefix.  The
// function will ask the user to confirm the passphrase and will repeat the
// prompts until they enter a matching response.
func PassPrompt(reader *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
	// Prompt the user until they enter a passphrase.
	prompt := fmt.Sprintf("%s: ", prefix)
	for {
		fmt.Print(prompt)
		var pass []byte
		var err error
		fd := int(os.Stdin.Fd())
		if terminal.IsTerminal(fd) {
			pass, err = terminal.ReadPassword(fd)
		} else {
			pass, err = reader.ReadBytes('\n')
			if err == io.EOF {
				err = nil
			}
		}
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		pass = bytes.TrimSpace(pass)
		if len(pass) == 0 {
			continue
		}

		if !confirm {
			return pass, nil
		}

		fmt.Print("Confirm passphrase: ")
		confirm, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			return nil, err
		}
		fmt.Print("\n")
		confirm = bytes.TrimSpace(confirm)
		if !bytes.Equal(pass, confirm) {
			fmt.Println("The entered passphrases do not match")
			continue
		}

		return pass, nil
	}
}

// PrivatePass prompts the user for a private passphrase.  All prompts are
// repeated until the user enters a valid response.
func PrivatePass(reader *bufio.Reader) ([]byte, error) {
	return PassPrompt(reader, "Enter the private passphrase for your new wallet", true)
}

// PublicPass prompts the user whether they want to add an additional layer of
// encryption to the wallet.  When the user answers yes and there is already a
// public passphrase provided via the passed config, it prompts them whether or
// not to use that configured passphrase.  It will also detect when the same
// passphrase is used for the private and public passphrase and prompt the user
// if they are sure they want to use the same passphrase for both.  Finally, all
// prompts are repeated until the user enters a valid response.
func PublicPass(reader *bufio.Reader, privPass []byte,
	defaultPubPassphrase, configPubPass []byte) ([]byte, error) {

	pubPass := defaultPubPassphrase
	usePubPass, err := promptListBool(reader, "Do you want "+
		"to add an additional layer of encryption for public "+
		"data?", "no")
	if err != nil {
		return nil, err
	}

	if !usePubPass {
		return pubPass, nil
	}

	if len(configPubPass) != 0 && !bytes.Equal(configPubPass, pubPass) {
		useExisting, err := promptListBool(reader, "Use the "+
			"existing configured public passphrase for encryption "+
			"of public data?", "no")
		if err != nil {
			return nil, err
		}

		if useExisting {
			return configPubPass, nil
		}
	}

	for {
		pubPass, err = PassPrompt(reader, "Enter the public "+
			"passphrase for your new wallet", true)
		if err != nil {
			return nil, err
		}

		if bytes.Equal(pubPass, privPass) {
			useSamePass, err := promptListBool(reader,
				"Are you sure want to use the same passphrase "+
					"for public and private data?", "no")
			if err != nil {
				return nil, err
			}

			if useSamePass {
				break
			}

			continue
		}

		break
	}

	fmt.Println("NOTE: Use the --walletpass option to configure your " +
		"public passphrase.")
	return pubPass, nil
}

// Seed prompts the user whether they want to use an existing wallet generation
// seed.  When the user answers no, a seed will be generated and displayed to
// the user along with prompting them for confirmation.  When the user answers
// yes, a the user is prompted for it.  All prompts are repeated until the user
// enters a valid response. The bool returned indicates if the wallet was
// restored from a given seed or not.
func Seed(reader *bufio.Reader) ([]byte, error) {
	// Ascertain the wallet generation seed.
	useUserSeed, err := promptListBool(reader, "Do you have an "+
		"existing wallet seed you want to use?", "no")
	if err != nil {
		return nil, err
	}
	if !useUserSeed {
		seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
		if err != nil {
			return nil, err
		}

		seedStrSplit := walletseed.EncodeMnemonicSlice(seed)

		fmt.Println("Your wallet generation seed is:")
		for i := 0; i < hdkeychain.RecommendedSeedLen+1; i++ {
			fmt.Printf("%v ", seedStrSplit[i])

			if (i+1)%6 == 0 {
				fmt.Printf("\n")
			}
		}

		fmt.Printf("\n\nHex: %x\n", seed)
		fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" +
			"will NOT be able to restore your wallet without it.")
		fmt.Println("Please keep in mind that anyone who has access\n" +
			"to the seed can also restore your wallet thereby\n" +
			"giving them access to all your funds, so it is\n" +
			"imperative that you keep it in a secure location.")

		for {
			fmt.Print(`Once you have stored the seed in a safe ` +
				`and secure location, enter "OK" to continue: `)
			confirmSeed, err := reader.ReadString('\n')
			if err != nil {
				return nil, err
			}
			confirmSeed = strings.TrimSpace(confirmSeed)
			confirmSeed = strings.Trim(confirmSeed, `"`)
			if strings.EqualFold("OK", confirmSeed) {
				break
			}
		}

		return seed, nil
	}

	for {
		fmt.Print("Enter existing wallet seed " +
			"(followed by a blank line): ")

		// Use scanner instead of buffio.Reader so we can choose choose
		// more complicated ending condition rather than just a single
		// newline.
		var seedStr string
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			line := scanner.Text()
			if line == "" {
				break
			}
			seedStr += " " + line
		}
		seedStrTrimmed := strings.TrimSpace(seedStr)
		seedStrTrimmed = collapseSpace(seedStrTrimmed)
		wordCount := strings.Count(seedStrTrimmed, " ") + 1

		var seed []byte
		if wordCount == 1 {
			if len(seedStrTrimmed)%2 != 0 {
				seedStrTrimmed = "0" + seedStrTrimmed
			}
			seed, err = hex.DecodeString(seedStrTrimmed)
			if err != nil {
				fmt.Printf("Input error: %v\n", err.Error())
			}
		} else {
			seed, err = walletseed.DecodeUserInput(seedStrTrimmed)
			if err != nil {
				fmt.Printf("Input error: %v\n", err.Error())
			}
		}
		if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
			len(seed) > hdkeychain.MaxSeedBytes {
			fmt.Printf("Invalid seed specified.  Must be a "+
				"word seed (usually 33 words) using the PGP wordlist or "+
				"hexadecimal value that is at least %d bits and "+
				"at most %d bits\n", hdkeychain.MinSeedBytes*8,
				hdkeychain.MaxSeedBytes*8)
			continue
		}

		fmt.Printf("\nSeed input successful. \nHex: %x\n", seed)

		return seed, nil
	}
}

// Setup prompts for, from a buffered reader, the private and/or public
// encryption passphrases to secure a wallet and a previously derived wallet
// seed to use, if any.  privPass and pubPass will always be non-nil values
// (private encryption is required and choosing to not use public data
// encryption will still encrypt the data with an insecure default), and a
// randomly generated seed of the recommended length will be generated and
// returned after the user has confirmed the seed has been backed up to a secure
// location.
//
// The configPubPass parameter is optional (nil should be used to represent the
// lack of a value).  When non-nil, this value represents a public passphrase
// previously specified in a configuration file.  The user will be given the
// option of using this passphrase if public data encryption is enabled,
// otherwise a user-specified passphrase will be prompted for.
func Setup(r *bufio.Reader, insecurePubPass, configPubPass []byte) (privPass, pubPass, seed []byte, err error) {
	// Decred: no legacy keystore restore is needed (first decred wallet
	// version did not use the legacy keystore from earlier versions of
	// btcwallet).

	// Start by prompting for the private passphrase.  When there is an
	// existing keystore, the user will be promped for that passphrase,
	// otherwise they will be prompted for a new one.
	privPass, err = PrivatePass(r)
	if err != nil {
		return
	}

	// Ascertain the public passphrase.  This will either be a value
	// specified by the user or the default hard-coded public passphrase if
	// the user does not want the additional public data encryption.
	pubPass, err = PublicPass(r, privPass, insecurePubPass, configPubPass)
	if err != nil {
		return
	}

	// Ascertain the wallet generation seed.  This will either be an
	// automatically generated value the user has already confirmed or a
	// value the user has entered which has already been validated.
	seed, err = Seed(r)

	return
}

// collapseSpace takes a string and replaces any repeated areas of whitespace
// with a single space character.
func collapseSpace(in string) string {
	whiteSpace := false
	out := ""
	for _, c := range in {
		if unicode.IsSpace(c) {
			if !whiteSpace {
				out = out + " "
			}
			whiteSpace = true
		} else {
			out = out + string(c)
			whiteSpace = false
		}
	}
	return out
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package server

import (
	"expvar"
	"net/http"

	"github.com/maier/go-appstats"
)

func (s *Server) router(w http.ResponseWriter, r *http.Request) {
	appstats.IncrementInt("requests_total")

	s.logger.Info().
		Str("method", r.Method).
		Str("url", r.URL.String()).
		Msg("Request")

	switch r.Method {
	case "GET":
		if pluginPathRx.MatchString(r.URL.Path) { // run plugin(s)
			s.logger.Debug().Msg("calling run")
			s.run(w, r)
			s.logger.Debug().Msg("run complete")
		} else if inventoryPathRx.MatchString(r.URL.Path) { // plugin inventory
			s.inventory(w, r)
		} else if statsPathRx.MatchString(r.URL.Path) { // app stats
			expvar.Handler().ServeHTTP(w, r)
		} else if promPathRx.MatchString(r.URL.Path) { // output prom format...
			s.promOutput(w, r)
		} else {
			appstats.IncrementInt("requests_bad")
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	case "POST":
		fallthrough
	case "PUT":
		if writePathRx.MatchString(r.URL.Path) {
			s.write(w, r)
		} else if promPathRx.MatchString(r.URL.Path) {
			s.promReceiver(w, r)
		} else {
			appstats.IncrementInt("requests_bad")
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	default:
		appstats.IncrementInt("requests_bad")
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}
<commit_msg>upd: switch server request logging to debug<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package server

import (
	"expvar"
	"net/http"

	"github.com/maier/go-appstats"
)

func (s *Server) router(w http.ResponseWriter, r *http.Request) {
	appstats.IncrementInt("requests_total")

	s.logger.Debug().
		Str("method", r.Method).
		Str("url", r.URL.String()).
		Msg("request")

	switch r.Method {
	case "GET":
		if pluginPathRx.MatchString(r.URL.Path) { // run plugin(s)
			s.logger.Debug().Msg("calling run")
			s.run(w, r)
			s.logger.Debug().Msg("run complete")
		} else if inventoryPathRx.MatchString(r.URL.Path) { // plugin inventory
			s.inventory(w, r)
		} else if statsPathRx.MatchString(r.URL.Path) { // app stats
			expvar.Handler().ServeHTTP(w, r)
		} else if promPathRx.MatchString(r.URL.Path) { // output prom format...
			s.promOutput(w, r)
		} else {
			appstats.IncrementInt("requests_bad")
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	case "POST":
		fallthrough
	case "PUT":
		if writePathRx.MatchString(r.URL.Path) {
			s.write(w, r)
		} else if promPathRx.MatchString(r.URL.Path) {
			s.promReceiver(w, r)
		} else {
			appstats.IncrementInt("requests_bad")
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	default:
		appstats.IncrementInt("requests_bad")
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package server 与路由有关的服务管理。
//
// NOTE: 测试内容涉及到 80 端口，部分系统可能需要 sudo go test 才能正确执行。
package server

import (
	"context"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/issue9/handlers"
	"github.com/issue9/logs"
	"github.com/issue9/mux"
)

// Server 服务器控制
type Server struct {
	conf *Config
	mux  *mux.ServeMux

	// 除了 mux 所依赖的 http.Server 实例之外，
	// 还有诸如 80 端口跳转等产生的 http.Server 实例。
	// 记录这些 server，方便重启等操作。
	servers []*http.Server
}

// New 声明一个新的 Server 实例
func New(conf *Config) (*Server, error) {
	if conf == nil {
		return nil, errors.New("参数 conf 不能为空")
	}

	if err := conf.Sanitize(); err != nil {
		return nil, err
	}

	return &Server{
		mux:     mux.NewServeMux(!conf.Options),
		servers: make([]*http.Server, 0, 5),
		conf:    conf,
	}, nil
}

// Mux 返回默认的 *mux.ServeMux 实例
func (s *Server) Mux() *mux.ServeMux {
	return s.mux
}

// 初始化路由项。
func (s *Server) initRoutes(h http.Handler) http.Handler {
	// 静态文件路由，在其它路由构建之前调用
	for url, dir := range s.conf.Static {
		if !strings.HasSuffix(url, "/") {
			url += "/"
		}
		s.mux.Get(url, http.StripPrefix(url, handlers.Compress(http.FileServer(http.Dir(dir)))))
	}

	if h == nil {
		h = s.mux
	}

	// 构建其它路由
	return s.buildHandler(h)
}

// Run 运行路由，执行监听程序。
//
// h 表示需要执行的路由处理函数，传递 nil 时，会自动以 Server.Mux() 代替。
// 可以通过以下方式，将一些 http.Handler 实例附加到 Server.Mux() 之上：
//  s.Run(handlers.Host(s.Mux(), "www.caixw.io")
func (s *Server) Run(h http.Handler) error {
	h = s.initRoutes(h)

	if s.conf.HTTPS {
		switch s.conf.HTTPState {
		case httpStateListen:
			go func() {
				logs.Infof("开始监听[%v]端口", httpPort)
				logs.Error(s.getServer(httpPort, h).ListenAndServe())
			}()
		case httpStateRedirect:
			go func() {
				logs.Infof("开始监听[%v]端口，并跳转至[%v]", httpPort, httpsPort)
				logs.Error(s.httpRedirectServer().ListenAndServe())
			}()
			// 空值或是 disable 均为默认处理方式，即不作为。
		}

		logs.Infof("开始监听[%v]端口", s.conf.Port)
		return s.getServer(s.conf.Port, h).ListenAndServeTLS(s.conf.CertFile, s.conf.KeyFile)
	}

	logs.Infof("开始监听[%v]端口", s.conf.Port)
	return s.getServer(s.conf.Port, h).ListenAndServe()
}

// Shutdown 关闭服务。
//
// timeout 若超过该时间，服务还未自动停止的，则会强制停止。
// 若 timeout<=0，则会立即停止服务；
// 若 timeout>0 时，则会等待处理完毕或是该时间耗尽才停止服务。
func (s *Server) Shutdown(timeout time.Duration) error {
	if timeout <= 0 {
		for _, srv := range s.servers {
			if err := srv.Close(); err != nil {
				return err
			}
		}
	} else {
		for _, srv := range s.servers {
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()

			if err := srv.Shutdown(ctx); err != nil {
				return err
			}
		}
	}

	s.servers = s.servers[:0]
	return nil
}

// 构建一个从 HTTP 跳转到 HTTPS 的路由服务。
func (s *Server) httpRedirectServer() *http.Server {
	return s.getServer(httpPort, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 构建跳转链接
		url := r.URL
		url.Scheme = "HTTPS"
		url.Host = strings.Split(r.Host, ":")[0] + s.conf.Port

		urlStr := url.String()
		http.Redirect(w, r, urlStr, http.StatusMovedPermanently)
	}))
}

// 获取 http.Server 实例，相对于 http 的默认实现，指定了 ErrorLog 字段。
func (s *Server) getServer(port string, h http.Handler) *http.Server {
	srv := &http.Server{
		Addr:         port,
		Handler:      h,
		ErrorLog:     logs.ERROR(),
		ReadTimeout:  s.conf.ReadTimeout,
		WriteTimeout: s.conf.WriteTimeout,
	}

	s.servers = append(s.servers, srv)

	return srv
}
<commit_msg>更新文档错误<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package server 与路由有关的服务管理。
//
// NOTE: 测试内容涉及到 80 端口，部分系统可能需要 sudo go test 才能正确执行。
package server

import (
	"context"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/issue9/handlers"
	"github.com/issue9/logs"
	"github.com/issue9/mux"
)

// Server 服务器控制
type Server struct {
	conf *Config
	mux  *mux.ServeMux

	// 除了 mux 所依赖的 http.Server 实例之外，
	// 还有诸如 80 端口跳转等产生的 http.Server 实例。
	// 记录这些 server，方便关闭服务等操作。
	servers []*http.Server
}

// New 声明一个新的 Server 实例
func New(conf *Config) (*Server, error) {
	if conf == nil {
		return nil, errors.New("参数 conf 不能为空")
	}

	if err := conf.Sanitize(); err != nil {
		return nil, err
	}

	return &Server{
		mux:     mux.NewServeMux(!conf.Options),
		servers: make([]*http.Server, 0, 5),
		conf:    conf,
	}, nil
}

// Mux 返回默认的 *mux.ServeMux 实例
func (s *Server) Mux() *mux.ServeMux {
	return s.mux
}

// 初始化路由项。
func (s *Server) initRoutes(h http.Handler) http.Handler {
	// 静态文件路由，在其它路由构建之前调用
	for url, dir := range s.conf.Static {
		if !strings.HasSuffix(url, "/") {
			url += "/"
		}
		s.mux.Get(url, http.StripPrefix(url, handlers.Compress(http.FileServer(http.Dir(dir)))))
	}

	if h == nil {
		h = s.mux
	}

	// 构建其它路由
	return s.buildHandler(h)
}

// Run 运行路由，执行监听程序。
//
// h 表示需要执行的路由处理函数，传递 nil 时，会自动以 Server.Mux() 代替。
// 可以通过以下方式，将一些 http.Handler 实例附加到 Server.Mux() 之上：
//  s.Run(handlers.Host(s.Mux(), "www.caixw.io")
func (s *Server) Run(h http.Handler) error {
	h = s.initRoutes(h)

	if s.conf.HTTPS {
		switch s.conf.HTTPState {
		case httpStateListen:
			go func() {
				logs.Infof("开始监听[%v]端口", httpPort)
				logs.Error(s.getServer(httpPort, h).ListenAndServe())
			}()
		case httpStateRedirect:
			go func() {
				logs.Infof("开始监听[%v]端口，并跳转至[%v]", httpPort, httpsPort)
				logs.Error(s.httpRedirectServer().ListenAndServe())
			}()
			// 空值或是 disable 均为默认处理方式，即不作为。
		}

		logs.Infof("开始监听[%v]端口", s.conf.Port)
		return s.getServer(s.conf.Port, h).ListenAndServeTLS(s.conf.CertFile, s.conf.KeyFile)
	}

	logs.Infof("开始监听[%v]端口", s.conf.Port)
	return s.getServer(s.conf.Port, h).ListenAndServe()
}

// Shutdown 关闭服务。
//
// timeout 若超过该时间，服务还未自动停止的，则会强制停止。
// 若 timeout<=0，则会立即停止服务；
// 若 timeout>0 时，则会等待处理完毕或是该时间耗尽才停止服务。
func (s *Server) Shutdown(timeout time.Duration) error {
	if timeout <= 0 {
		for _, srv := range s.servers {
			if err := srv.Close(); err != nil {
				return err
			}
		}
	} else {
		for _, srv := range s.servers {
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()

			if err := srv.Shutdown(ctx); err != nil {
				return err
			}
		}
	}

	s.servers = s.servers[:0]
	return nil
}

// 构建一个从 HTTP 跳转到 HTTPS 的路由服务。
func (s *Server) httpRedirectServer() *http.Server {
	return s.getServer(httpPort, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 构建跳转链接
		url := r.URL
		url.Scheme = "HTTPS"
		url.Host = strings.Split(r.Host, ":")[0] + s.conf.Port

		urlStr := url.String()
		http.Redirect(w, r, urlStr, http.StatusMovedPermanently)
	}))
}

// 获取 http.Server 实例，相对于 http 的默认实现，指定了 ErrorLog 字段。
func (s *Server) getServer(port string, h http.Handler) *http.Server {
	srv := &http.Server{
		Addr:         port,
		Handler:      h,
		ErrorLog:     logs.ERROR(),
		ReadTimeout:  s.conf.ReadTimeout,
		WriteTimeout: s.conf.WriteTimeout,
	}

	s.servers = append(s.servers, srv)

	return srv
}
<|endoftext|>


<commit_before>// Copyright 2020 The Ebiten Authors
//
// 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 shader

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"regexp"
	"sort"
	"strings"
)

const (
	varyingStructName = "VertexOut"
)

var (
	kageTagRe = regexp.MustCompile("^`" + `kage:\"(.+)\"` + "`$")
)

type variable struct {
	name     string
	typ      typ
	constant bool
	init     string
}

type Shader struct {
	// position is the field name of VertexOut that represents a vertex position (gl_Position in GLSL).
	position variable

	// varyings is a collection of varying variables.
	varyings []variable

	// uniforms is a collection of uniform variables.
	uniforms []variable

	// globals is a collection of global variables.
	globals []variable

	errs []string
}

type ParseError struct {
	errs []string
}

func (p *ParseError) Error() string {
	return strings.Join(p.errs, "\n")
}

func NewShader(src []byte) (*Shader, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "", src, parser.AllErrors)
	if err != nil {
		return nil, err
	}

	s := &Shader{}
	s.parse(f)

	if len(s.errs) > 0 {
		return nil, &ParseError{s.errs}
	}

	sort.Slice(s.varyings, func(a, b int) bool {
		return s.varyings[a].name < s.varyings[b].name
	})
	sort.Slice(s.uniforms, func(a, b int) bool {
		return s.uniforms[a].name < s.uniforms[b].name
	})
	sort.Slice(s.globals, func(a, b int) bool {
		return s.globals[a].name < s.globals[b].name
	})

	// TODO: Make a call graph and reorder the elements.
	return s, nil
}

func (s *Shader) addError(str string) {
	s.errs = append(s.errs, str)
}

func (s *Shader) parse(f *ast.File) {
	// TODO: Accumulate errors
	for name, obj := range f.Scope.Objects {
		switch name {
		case varyingStructName:
			s.parseVaryingStruct(obj)
		default:
			switch obj.Kind {
			case ast.Con:
				s.parsePackageLevelConstant(name, obj)
			case ast.Var:
				s.parsePackageLevelVariable(name, obj)
			}
		}
	}
}

func (sh *Shader) parseVaryingStruct(obj *ast.Object) {
	name := obj.Name
	if obj.Kind != ast.Typ {
		sh.addError(fmt.Sprintf("%s must be a type but %s", name, obj.Kind))
		return
	}
	t := obj.Decl.(*ast.TypeSpec).Type
	s, ok := t.(*ast.StructType)
	if !ok {
		sh.addError(fmt.Sprintf("%s must be a struct but not", name))
		return
	}

	for _, f := range s.Fields.List {
		if f.Tag != nil {
			tag := f.Tag.Value
			m := kageTagRe.FindStringSubmatch(tag)
			if m == nil {
				sh.addError(fmt.Sprintf("invalid struct tag: %s", tag))
				continue
			}
			if m[1] != "position" {
				sh.addError(fmt.Sprintf("struct tag value must be position in %s but %s", varyingStructName, m[1]))
				continue
			}
			if len(f.Names) != 1 {
				sh.addError(fmt.Sprintf("position members must be one"))
				continue
			}
			t, err := parseType(f.Type)
			if err != nil {
				sh.addError(err.Error())
				continue
			}
			if t != typVec4 {
				sh.addError(fmt.Sprintf("position must be vec4 but %s", t))
				continue
			}
			sh.position = variable{
				name: f.Names[0].Name,
				typ:  t,
			}
			continue
		}
		t, err := parseType(f.Type)
		if err != nil {
			sh.addError(err.Error())
			continue
		}
		if !t.numeric() {
			sh.addError(fmt.Sprintf("members in %s must be numeric but %s", varyingStructName, t))
			continue
		}
		for _, n := range f.Names {
			sh.varyings = append(sh.varyings, variable{
				name: n.Name,
				typ:  t,
			})
		}
	}
}

func (s *Shader) parsePackageLevelVariable(name string, obj *ast.Object) {
	v, ok := obj.Decl.(*ast.ValueSpec)
	if !ok {
		s.addError("value spec expected")
		return
	}
	t, err := parseType(v.Type)
	if err != nil {
		s.addError(err.Error())
		return
	}
	val := variable{
		name: name,
		typ:  t,
	}
	// TODO: Parse initial value.
	if 'A' <= name[0] && name[0] <= 'Z' {
		s.uniforms = append(s.uniforms, val)
	} else {
		s.globals = append(s.globals, val)
	}
}

func (s *Shader) parsePackageLevelConstant(name string, obj *ast.Object) {
	vs, ok := obj.Decl.(*ast.ValueSpec)
	if !ok {
		s.addError("value spec expected")
		return
	}
	t, err := parseType(vs.Type)
	if err != nil {
		s.addError(err.Error())
		return
	}
	for i, v := range vs.Values {
		if vs.Names[i].Name != name {
			continue
		}

		var init string
		switch v := v.(type) {
		case *ast.BasicLit:
			if v.Kind != token.INT && v.Kind != token.FLOAT {
				s.addError(fmt.Sprintf("literal must be int or float but %s", v.Kind))
				return
			}
			init = v.Value // TODO: This should be math/big.Int or Float.
		default:
			// TODO: Parse the expression.
		}
		val := variable{
			name:     name,
			typ:      t, // TODO: Treat consts without types
			constant: true,
			init:     init,
		}
		s.globals = append(s.globals, val)
	}
}

// Dump dumps the shader state in an intermediate language.
func (s *Shader) Dump() string {
	var lines []string

	lines = append(lines, fmt.Sprintf("var %s varying %s // position", s.position.name, s.position.typ))
	for _, v := range s.varyings {
		lines = append(lines, fmt.Sprintf("var %s varying %s", v.name, v.typ))
	}

	for _, u := range s.uniforms {
		lines = append(lines, fmt.Sprintf("var %s uniform %s", u.name, u.typ))
	}

	for _, g := range s.globals {
		prefix := "var"
		if g.constant {
			prefix = "const"
		}
		init := ""
		if g.init != "" {
			init = " = " + g.init
		}
		lines = append(lines, fmt.Sprintf("%s %s %s%s", prefix, g.name, g.typ, init))
	}

	return strings.Join(lines, "\n") + "\n"
}

func (s *Shader) GlslVertex() string {
	var lines []string

	for _, v := range s.varyings {
		// TODO: variable names must be escaped not to conflict with keywords.
		lines = append(lines, fmt.Sprintf("varying %s %s;", v.typ.glslString(), v.name))
	}
	return strings.Join(lines, "\n") + "\n"
}
<commit_msg>shader: Fix comments<commit_after>// Copyright 2020 The Ebiten Authors
//
// 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 shader

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"regexp"
	"sort"
	"strings"
)

const (
	varyingStructName = "VertexOut"
)

var (
	kageTagRe = regexp.MustCompile("^`" + `kage:\"(.+)\"` + "`$")
)

type variable struct {
	name     string
	typ      typ
	constant bool
	init     string
}

type Shader struct {
	// position is the field name of VertexOut that represents a vertex position (gl_Position in GLSL).
	position variable

	// varyings is a collection of varying variables.
	varyings []variable

	// uniforms is a collection of uniform variables.
	uniforms []variable

	// globals is a collection of global variables.
	globals []variable

	errs []string
}

type ParseError struct {
	errs []string
}

func (p *ParseError) Error() string {
	return strings.Join(p.errs, "\n")
}

func NewShader(src []byte) (*Shader, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "", src, parser.AllErrors)
	if err != nil {
		return nil, err
	}

	s := &Shader{}
	s.parse(f)

	if len(s.errs) > 0 {
		return nil, &ParseError{s.errs}
	}

	sort.Slice(s.varyings, func(a, b int) bool {
		return s.varyings[a].name < s.varyings[b].name
	})
	sort.Slice(s.uniforms, func(a, b int) bool {
		return s.uniforms[a].name < s.uniforms[b].name
	})
	sort.Slice(s.globals, func(a, b int) bool {
		return s.globals[a].name < s.globals[b].name
	})

	// TODO: Make a call graph and reorder the elements.
	return s, nil
}

func (s *Shader) addError(str string) {
	// TODO: Add token positions.
	s.errs = append(s.errs, str)
}

func (s *Shader) parse(f *ast.File) {
	for name, obj := range f.Scope.Objects {
		switch name {
		case varyingStructName:
			s.parseVaryingStruct(obj)
		default:
			switch obj.Kind {
			case ast.Con:
				s.parsePackageLevelConstant(name, obj)
			case ast.Var:
				s.parsePackageLevelVariable(name, obj)
			}
		}
	}
}

func (sh *Shader) parseVaryingStruct(obj *ast.Object) {
	name := obj.Name
	if obj.Kind != ast.Typ {
		sh.addError(fmt.Sprintf("%s must be a type but %s", name, obj.Kind))
		return
	}
	t := obj.Decl.(*ast.TypeSpec).Type
	s, ok := t.(*ast.StructType)
	if !ok {
		sh.addError(fmt.Sprintf("%s must be a struct but not", name))
		return
	}

	for _, f := range s.Fields.List {
		if f.Tag != nil {
			tag := f.Tag.Value
			m := kageTagRe.FindStringSubmatch(tag)
			if m == nil {
				sh.addError(fmt.Sprintf("invalid struct tag: %s", tag))
				continue
			}
			if m[1] != "position" {
				sh.addError(fmt.Sprintf("struct tag value must be position in %s but %s", varyingStructName, m[1]))
				continue
			}
			if len(f.Names) != 1 {
				sh.addError(fmt.Sprintf("position members must be one"))
				continue
			}
			t, err := parseType(f.Type)
			if err != nil {
				sh.addError(err.Error())
				continue
			}
			if t != typVec4 {
				sh.addError(fmt.Sprintf("position must be vec4 but %s", t))
				continue
			}
			sh.position = variable{
				name: f.Names[0].Name,
				typ:  t,
			}
			continue
		}
		t, err := parseType(f.Type)
		if err != nil {
			sh.addError(err.Error())
			continue
		}
		if !t.numeric() {
			sh.addError(fmt.Sprintf("members in %s must be numeric but %s", varyingStructName, t))
			continue
		}
		for _, n := range f.Names {
			sh.varyings = append(sh.varyings, variable{
				name: n.Name,
				typ:  t,
			})
		}
	}
}

func (s *Shader) parsePackageLevelVariable(name string, obj *ast.Object) {
	v, ok := obj.Decl.(*ast.ValueSpec)
	if !ok {
		s.addError("value spec expected")
		return
	}
	t, err := parseType(v.Type)
	if err != nil {
		s.addError(err.Error())
		return
	}
	val := variable{
		name: name,
		typ:  t,
	}
	// TODO: Parse initial value.
	if 'A' <= name[0] && name[0] <= 'Z' {
		s.uniforms = append(s.uniforms, val)
	} else {
		s.globals = append(s.globals, val)
	}
}

func (s *Shader) parsePackageLevelConstant(name string, obj *ast.Object) {
	vs, ok := obj.Decl.(*ast.ValueSpec)
	if !ok {
		s.addError("value spec expected")
		return
	}
	t, err := parseType(vs.Type)
	if err != nil {
		s.addError(err.Error())
		return
	}
	for i, v := range vs.Values {
		if vs.Names[i].Name != name {
			continue
		}

		var init string
		switch v := v.(type) {
		case *ast.BasicLit:
			if v.Kind != token.INT && v.Kind != token.FLOAT {
				s.addError(fmt.Sprintf("literal must be int or float but %s", v.Kind))
				return
			}
			init = v.Value // TODO: This should be math/big.Int or Float.
		default:
			// TODO: Parse the expression.
		}
		val := variable{
			name:     name,
			typ:      t, // TODO: Treat consts without types
			constant: true,
			init:     init,
		}
		s.globals = append(s.globals, val)
	}
}

// Dump dumps the shader state in an intermediate language.
func (s *Shader) Dump() string {
	var lines []string

	lines = append(lines, fmt.Sprintf("var %s varying %s // position", s.position.name, s.position.typ))
	for _, v := range s.varyings {
		lines = append(lines, fmt.Sprintf("var %s varying %s", v.name, v.typ))
	}

	for _, u := range s.uniforms {
		lines = append(lines, fmt.Sprintf("var %s uniform %s", u.name, u.typ))
	}

	for _, g := range s.globals {
		prefix := "var"
		if g.constant {
			prefix = "const"
		}
		init := ""
		if g.init != "" {
			init = " = " + g.init
		}
		lines = append(lines, fmt.Sprintf("%s %s %s%s", prefix, g.name, g.typ, init))
	}

	return strings.Join(lines, "\n") + "\n"
}

func (s *Shader) GlslVertex() string {
	var lines []string

	for _, v := range s.varyings {
		// TODO: variable names must be escaped not to conflict with keywords.
		lines = append(lines, fmt.Sprintf("varying %s %s;", v.typ.glslString(), v.name))
	}
	return strings.Join(lines, "\n") + "\n"
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// 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 ztest

import (
	"log"
	"os"
	"strconv"
	"time"
)

var _timeoutScale = 1.0

// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.
func Timeout(base time.Duration) time.Duration {
	return time.Duration(float64(base) * _timeoutScale)
}

// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.
func Sleep(base time.Duration) {
	time.Sleep(Timeout(base))
}

// Initialize checks the environment and alters the timeout scale accordingly.
// It returns a function to undo the scaling.
func Initialize(factor string) func() {
	original := _timeoutScale
	fv, err := strconv.ParseFloat(factor, 64)
	if err != nil {
		panic(err)
	}
	_timeoutScale = fv
	return func() { _timeoutScale = original }
}

func init() {
	if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" {
		Initialize(v)
		log.Printf("Scaling timeouts by %vx.\n", _timeoutScale)
	}
}
<commit_msg>chore(ztest/timeout): capture original scale after err handling (#1138)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// 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 ztest

import (
	"log"
	"os"
	"strconv"
	"time"
)

var _timeoutScale = 1.0

// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.
func Timeout(base time.Duration) time.Duration {
	return time.Duration(float64(base) * _timeoutScale)
}

// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.
func Sleep(base time.Duration) {
	time.Sleep(Timeout(base))
}

// Initialize checks the environment and alters the timeout scale accordingly.
// It returns a function to undo the scaling.
func Initialize(factor string) func() {
	fv, err := strconv.ParseFloat(factor, 64)
	if err != nil {
		panic(err)
	}
	original := _timeoutScale
	_timeoutScale = fv
	return func() { _timeoutScale = original }
}

func init() {
	if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" {
		Initialize(v)
		log.Printf("Scaling timeouts by %vx.\n", _timeoutScale)
	}
}
<|endoftext|>


<commit_before>package x86db

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
)

// DB holds the list of known instructions
type DB struct {
	instructionsFile string
	Instructions     InstructionSlice
}

func NewDB() *DB {
	return NewDBFomFile("")
}

func NewDBFomFile(instructionsFile string) *DB {
	return &DB{
		instructionsFile: instructionsFile,
	}
}

var ignoreFlags = []string{
	"ignore", "OBSOLETE",
}

func ignoreInstruction(flag string) bool {
	for _, f := range ignoreFlags {
		if f == flag {
			return true
		}
	}
	return false
}

func (db *DB) readInstructions(r io.Reader) error {
	pattern := regexp.MustCompile(`^\s*(\S+)\s+(\S+)\s+(\S+|\[.*\])\s+(\S+)\s*$`)

	scanner := bufio.NewScanner(r)
next:
	for scanner.Scan() {
		line := scanner.Text()

		// strip comments
		idx := strings.IndexRune(line, ';')
		if idx >= 0 {
			line = line[0:idx]
		}

		if line == "" {
			continue
		}

		fields := pattern.FindSubmatch([]byte(line))
		// We want 4 fields
		if len(fields) != 5 {
			return fmt.Errorf("readInstructions: expected 4 fields got %d", len(fields)-1)
		}

		// The 4th field holds misc, comma separated, flags.
		var opSizeFlags OpSize
		var extension Extension
		for _, field := range strings.Split(string(fields[4]), ",") {
			if ignoreInstruction(field) {
				goto next
			}

			f, err := opSizeFromString(field)
			if err == nil {
				opSizeFlags |= f
				continue
			}

			e, err := ExtensionFromString(field)
			if err == nil {
				extension = e
				continue
			}
		}

		instruction := Instruction{
			Name:      string(fields[1]),
			Operands:  strings.Split(string(fields[2]), ","),
			Pattern:   string(fields[3]),
			Flags:     string(fields[4]),
			Extension: extension,
			OpSize:    opSizeFlags,
		}
		db.Instructions = append(db.Instructions, instruction)
	}

	return nil
}

// Open loads instructions from disk.
func (db *DB) Open() error {
	var r io.Reader

	if db.instructionsFile == "" {
		// Use the insns.dat bundled with the package
		data, err := Asset("data/insns.dat")
		if err != nil {
			return err
		}

		r = bytes.NewReader(data)
	} else {
		// db file provided by the user
		f, err := os.Open(db.instructionsFile)
		if err != nil {
			return err
		}
		defer f.Close()

		r = f
	}

	if err := db.readInstructions(r); err != nil {
		return err
	}

	return nil
}

// Close closes the DB precious resources.
func (db *DB) Close() {

}

// FindByExtension returns the list of instructions introduced as part of the
// specificied extension.
func (db *DB) FindByExtension(extension Extension) InstructionSlice {
	return db.Instructions.Where(func(insn Instruction) bool {
		return insn.Extension == extension
	})
}
<commit_msg>db: Add documentation for NewDB or NewDBFromFile<commit_after>package x86db

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
)

// DB holds the list of known instructions
type DB struct {
	instructionsFile string
	Instructions     InstructionSlice
}

// NewDB creates a new DB object.
func NewDB() *DB {
	return NewDBFromFile("")
}

// NewDBFromFile creates a new DB object, loading the list of instructions from
// instructionsFile. The format of instructionsFile is the nasm one.
func NewDBFromFile(instructionsFile string) *DB {
	return &DB{
		instructionsFile: instructionsFile,
	}
}

var ignoreFlags = []string{
	"ignore", "OBSOLETE",
}

func ignoreInstruction(flag string) bool {
	for _, f := range ignoreFlags {
		if f == flag {
			return true
		}
	}
	return false
}

func (db *DB) readInstructions(r io.Reader) error {
	pattern := regexp.MustCompile(`^\s*(\S+)\s+(\S+)\s+(\S+|\[.*\])\s+(\S+)\s*$`)

	scanner := bufio.NewScanner(r)
next:
	for scanner.Scan() {
		line := scanner.Text()

		// strip comments
		idx := strings.IndexRune(line, ';')
		if idx >= 0 {
			line = line[0:idx]
		}

		if line == "" {
			continue
		}

		fields := pattern.FindSubmatch([]byte(line))
		// We want 4 fields
		if len(fields) != 5 {
			return fmt.Errorf("readInstructions: expected 4 fields got %d", len(fields)-1)
		}

		// The 4th field holds misc, comma separated, flags.
		var opSizeFlags OpSize
		var extension Extension
		for _, field := range strings.Split(string(fields[4]), ",") {
			if ignoreInstruction(field) {
				goto next
			}

			f, err := opSizeFromString(field)
			if err == nil {
				opSizeFlags |= f
				continue
			}

			e, err := ExtensionFromString(field)
			if err == nil {
				extension = e
				continue
			}
		}

		instruction := Instruction{
			Name:      string(fields[1]),
			Operands:  strings.Split(string(fields[2]), ","),
			Pattern:   string(fields[3]),
			Flags:     string(fields[4]),
			Extension: extension,
			OpSize:    opSizeFlags,
		}
		db.Instructions = append(db.Instructions, instruction)
	}

	return nil
}

// Open loads instructions from disk.
func (db *DB) Open() error {
	var r io.Reader

	if db.instructionsFile == "" {
		// Use the insns.dat bundled with the package
		data, err := Asset("data/insns.dat")
		if err != nil {
			return err
		}

		r = bytes.NewReader(data)
	} else {
		// db file provided by the user
		f, err := os.Open(db.instructionsFile)
		if err != nil {
			return err
		}
		defer f.Close()

		r = f
	}

	if err := db.readInstructions(r); err != nil {
		return err
	}

	return nil
}

// Close closes the DB precious resources.
func (db *DB) Close() {

}

// FindByExtension returns the list of instructions introduced as part of the
// specificied extension.
func (db *DB) FindByExtension(extension Extension) InstructionSlice {
	return db.Instructions.Where(func(insn Instruction) bool {
		return insn.Extension == extension
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"gopkg.in/mgo.v2/bson"

	mgo "gopkg.in/mgo.v2"
)

type database interface {
	// LoadURL loads a URL that was saved previously.
	LoadURL(name string) (string, error)

	// SaveURL saves a URL keyed by a name to be loaded later.
	SaveURL(name string, url string) error
}

// A NotFoundError is triggered if a name does not resolve to an URL in the
// database.
type NotFoundError struct {
	Name string
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("no URL found with name %q", e.Name)
}

type mongoDatabase struct {
	// URL is the URL to connect to the MongoDB:
	//   [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
	URL string

	dialed *mgo.Session
}

func (d *mongoDatabase) session() (*mgo.Session, error) {
	if d.dialed == nil {
		var err error
		d.dialed, err = mgo.Dial(d.URL)
		return d.dialed, err
	}
	return d.dialed.Copy(), nil
}

func (d *mongoDatabase) collection() (*mgo.Collection, error) {
	s, err := d.session()
	if err != nil {
		return nil, err
	}
	return s.DB("").C("shortURL"), nil
}

func (d *mongoDatabase) LoadURL(name string) (string, error) {
	c, err := d.collection()
	if err != nil {
		return "", err
	}
	var r bson.D
	c.Find(bson.D{{"name", name}}).One(&r)
	m := r.Map()
	if n := m["name"]; n == nil {
		return "", NotFoundError{name}
	}
	if url, ok := m["url"].(string); ok {
		return url, nil
	}
	return "", fmt.Errorf("Name is used but with a weird url object: %#v", m["url"])
}

func (d *mongoDatabase) SaveURL(name string, url string) error {
	c, err := d.collection()
	if err != nil {
		return err
	}
	c.Insert(bson.D{{"name", name}, {"url", url}})
	return nil
}
<commit_msg>Use _id for the name as it's unique and a true key of what's stored.<commit_after>package main

import (
	"fmt"

	"gopkg.in/mgo.v2/bson"

	mgo "gopkg.in/mgo.v2"
)

type database interface {
	// LoadURL loads a URL that was saved previously.
	LoadURL(name string) (string, error)

	// SaveURL saves a URL keyed by a name to be loaded later.
	SaveURL(name string, url string) error
}

// A NotFoundError is triggered if a name does not resolve to an URL in the
// database.
type NotFoundError struct {
	Name string
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("no URL found with name %q", e.Name)
}

type mongoDatabase struct {
	// URL is the URL to connect to the MongoDB:
	//   [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
	URL string

	dialed *mgo.Session
}

func (d *mongoDatabase) session() (*mgo.Session, error) {
	if d.dialed == nil {
		var err error
		d.dialed, err = mgo.Dial(d.URL)
		return d.dialed, err
	}
	return d.dialed.Copy(), nil
}

func (d *mongoDatabase) collection() (*mgo.Collection, error) {
	s, err := d.session()
	if err != nil {
		return nil, err
	}
	return s.DB("").C("shortURL"), nil
}

func (d *mongoDatabase) LoadURL(name string) (string, error) {
	c, err := d.collection()
	if err != nil {
		return "", err
	}
	var r bson.D
	c.FindId(name).One(&r)
	if len(r) == 0 {
		return "", NotFoundError{name}
	}
	url := r.Map()["url"]
	if s, ok := url.(string); ok {
		return s, nil
	}
	return "", fmt.Errorf("Name is used but with a weird url object: %#v", url)
}

func (d *mongoDatabase) SaveURL(name string, url string) error {
	c, err := d.collection()
	if err != nil {
		return err
	}
	c.Insert(bson.D{{"_id", name}, {"url", url}})
	return nil
}
<|endoftext|>